Генерация произвольной аватарки под разные форумы

Тег C#ASP.NET

На разных форумах и соц сетях требуют аватарки разных размеров, форматов, и прочей фигни. Зачем лезть в редактор, хранить где-то по пять копий одной и той-же картинки, если можно заставить сервак делать это за нас?

Фозьмем например Форум Васи Пупкина. На его форуме можно разместить аватарку размером не выше 80х80. У нас 100х100 к тому-же в png. Первым решением становится юзать скрипт с GET параметрами типа generate_avatar.aspx?width=n&height=y однако форумы на это решение умнее тебя и банят подобные пакости. Но можно обхитрить эту систему простой перезаписью URL в народе называемой «красивыми ссылками».

В красивых ссылках нам поможет URL Rewriter для IIS. Дополнение безплатно и пользуюсь им более полугода. Для других, в том числе и под апач/mod_mono(aspnet)/php, лучше гляньте в документацию. В конечном итоге, нам нужно перезаписать что-то вроде /avatargen/высота-ширина.формат в /avatargen.axd?width=&height&format

Сам генератор делается как HTTP хэндлер (это типа как модуль, только работает немного по другому, об этом пишется в МСДН). Так вот, делаем такой хэндлер:

using System.Linq;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

/// <summary>
/// Класс произвольной генерации автарок
/// </summary>
public class AvatarGen : IHttpHandler {
    public AvatarGen() {
    }
    public bool IsReusable {
        get { return true; }
    }
    public void ProcessRequest(HttpContext context) {
        // Где хранится исходное изображение. Лучше всего в хорошем качестве и как минимум 100х100
        const string AVSOURCE = "~/av.png";
        /*
         * Формат строки: /avatargen/<ширина>-<высота>.<формат:png|jpg|gif>
         * перенаправится в:
         * width=
         * height=
         * format=
         */
        if (
            string.IsNullOrEmpty(context.Request.QueryString["width"]) ||
            string.IsNullOrEmpty(context.Request.QueryString["height"]) ||
string.IsNullOrEmpty(context.Request.QueryString["format"]))
            return;
        // отпарсить верные значения
        int width = 0, height = 0;
        string format = context.Request.QueryString["format"];
        if (!int.TryParse(context.Request.QueryString["width"], out width)) return;
        if (!int.TryParse(context.Request.QueryString["height"], out height)) return;
        if (width <= 0 || height <= 0) return; // что за бред? как можно иметь картинку высотой/шириной в 0 пикселей?
        // проверка верных форматов
        switch (format) {
            case "png":
            case "jpg":
            case "gif":
                break;
            default:
                return;
        }

        // загрузить основную картинку
        using (MemoryStream msCache = new MemoryStream()) {
            using (Image imgOriginal = Image.FromFile(context.Server.MapPath(AVSOURCE))) {
                // рисуем новую
                using (Image bmpNew = new Bitmap(width, height)) {
                    Graphics g = Graphics.FromImage(bmpNew);
                    // высокое качество
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                    g.DrawImage(imgOriginal, new Rectangle(0, 0, width, height));
                    g.Flush();

                    // для жипега
                    EncoderParameter qltyParam = new EncoderParameter(Encoder.Quality, 90L);
                    EncoderParameters @params = new EncoderParameters(1);
                    @params.Param[0] = qltyParam;
                    ImageCodecInfo qltyCodec = ImageCodecInfo.GetImageEncoders().Where(e => e.MimeType.Equals("image/jpeg")).Single();
                    switch (format) {
                        case "png":
                            context.Response.ContentType = "image/png";
                            bmpNew.Save(msCache, imgOriginal.RawFormat); // если у вас что-то другое кроме ПНГ, лучше изменить
                            break;
                        case "jpg":
                            context.Response.ContentType = "image/jpeg";
                            bmpNew.Save(msCache, qltyCodec, @params);
                            break;
                        case "gif":
                            context.Response.ContentType = "image/gif";
                            bmpNew.Save(msCache, ImageFormat.Gif);
                            break;
                        default:
                            return;
                    }
                }
            }
            context.Response.Clear();
            // вывод клиенту
            msCache.WriteTo(context.Response.OutputStream);
        }
        context.Response.End();
    }
}

Затем, в web.config прописываем следующее в секции system.webServer/handlers (IIS7 и выше, в шестерке похожее но в system.web/httphandlers):

<add name="Генератор Аватара" verb="GET" path="avatargen.axd" type="AvatarGen" />

И в конце-концов для преобразователя ссылок (system.webServer/rewrite/rules):

         <rule name="RewriteUserFriendlyURL2" stopProcessing="true">
          <match url="^avatargen/([\d]+)-([\d]+)\.(png|jpg|gif)?$" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="false" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="false" />
          </conditions>
          <action type="Rewrite" url="avatargen.axd?width={R:1}&amp;height={R:2}&amp;format={R:3}" />
         </rule>

И имеем результат:

 

Пока не нашел ни одного места, где мне сказали, что ссылка левая.