.NET 4 Решение проблемы пересылки вложений с длинными рускими именами

При работе над одним из проектов у нас возникла необходимость пересылки в виде почтовых вложений файлов с длинными русскими именами. В основном эти файлы представляли собой документы в форматах Microsoft Word, Microsoft Excel, Microsoft PowerPoint и Adobe PDF.

Работая на платформе .NET 4 мы попытались воспользоваться стандартными классами объектов для отправки почты System.Net.Mail.MailMessage и System.Net.Mail.SmtpClient. Однако, нас ждало разочарование... Имена файлов в посланных сообщениях оказались нечитаемыми (см. скриншот).

Согласно сообщению сотрудника Microsoft с ником Tratcher на форуме .NET Framework Developer Center, эта ошибка известна Microsoft и связана она с тем, что SmtpClient теперь подчиняется правилам (RFC), ограничиваюшим длину строк в сообщениях до 76 символов, однако алгоритм кодировки имён файлов "забыли" подчинить этим правилам. Затем, господин (или госпожа?) Tratcher посоветовал подождать следующего релиза .NET Framework с исправленной функцией кодировки.

К сачастью на MSDN обнаружилось решение этой проблемы, опубликованное Marcel Roma, позволяющее обойти эту проблему за счёт использования factory-класса для создания почтовых вложений. Исходный код класса приводится ниже:

 

public class AttachmentHelper
{
    public static Attachment CreateAttachment(string attachmentFile, string displayName, TransferEncoding transferEncoding)
    {
        Attachment attachment = new Attachment(attachmentFile);
        attachment.TransferEncoding = transferEncoding;

        string tranferEncodingMarker = String.Empty;
        string encodingMarker = String.Empty;
        int maxChunkLength = 0;

        switch (transferEncoding)
        {
            case TransferEncoding.Base64:
                tranferEncodingMarker = "B";
                encodingMarker = "UTF-8";
                maxChunkLength = 30;
                break;
            case TransferEncoding.QuotedPrintable:
                tranferEncodingMarker = "Q";
                encodingMarker = "ISO-8859-1";
                maxChunkLength = 76;
                break;
            default:
                throw (new ArgumentException(String.Format("The specified TransferEncoding is not supported: {0}", transferEncoding, "transferEncoding")));
        }

        attachment.NameEncoding = Encoding.GetEncoding(encodingMarker);

        string encodingtoken = String.Format("=?{0}?{1}?", encodingMarker, tranferEncodingMarker);
        string softbreak = "?=";
        string encodedAttachmentName = encodingtoken;

        if (attachment.TransferEncoding == TransferEncoding.QuotedPrintable)
            encodedAttachmentName = HttpUtility.UrlEncode(displayName, Encoding.Default).Replace("+", " ").Replace("%", "=");
        else
            encodedAttachmentName = Convert.ToBase64String(Encoding.UTF8.GetBytes(displayName));

        encodedAttachmentName = SplitEncodedAttachmentName(encodingtoken, softbreak, maxChunkLength, encodedAttachmentName);
        attachment.Name = encodedAttachmentName;

        return attachment;
    }

    private static string SplitEncodedAttachmentName(string encodingtoken, string softbreak, int maxChunkLength, string encoded)
    {
        int splitLength = maxChunkLength - encodingtoken.Length - (softbreak.Length * 2);
        var parts = SplitByLength(encoded, splitLength);

        string encodedAttachmentName = encodingtoken;

        foreach (var part in parts)
            encodedAttachmentName += part + softbreak + encodingtoken;

        encodedAttachmentName = encodedAttachmentName.Remove(encodedAttachmentName.Length - encodingtoken.Length, encodingtoken.Length);
        return encodedAttachmentName;
    }

    private static IEnumerable<string> SplitByLength(string stringToSplit, int length)
    {
        while (stringToSplit.Length > length)
        {
            yield return stringToSplit.Substring(0, length);
            stringToSplit = stringToSplit.Substring(length);
        }

        if (stringToSplit.Length > 0) yield return stringToSplit;
    }
}

Скачать в скомпилированном виде.

Теперь, чтобы добавить вложение к письму мы просто должны создать его статической функцией  CreateAttachment(string attachmentFile, string displayName, TransferEncoding transferEncoding) перед добавлением к System.Net.Mail.MailMessag

Результат на скриншоте ниже: