Отправка электронной почты с вложениями с оптимизированными формами в 2025 году

Большинство статей о добавлении вложений к оптимизированным формам электронных писем примерно с 2019 года и не охватывают самые последние версии. Этот пост проходит через мою реализацию 2025 года и извлеченные уроки. Ваш пробег может варьироваться.


Проблема

Оптимизируемые формы встроенные SendEmailAftersUbmissionActor Отправляет чистые, простые электронные письма после отправки формы. Когда вы используете Загрузка файла блок элемента элементаАктер включает в себя ссылки в электронном письме, где хранятся файлы в CMS.

Это хорошо для внутренних пользователей с CMS -доступом. Но если ваши получатели внешний – Клиенты, партнеры, поставщики – эти ссылки не будут работать для них.
Мне нужно было:


Почему подклассник не сработает

Оптимизируемые SendEmailAftersUbmissionActor только позволяет ограничить подкласс.
Многие из ключевых операций частныйнет защищени Mimemessage никогда не передается для настройки.

Это означает, что нет способа просто «зацепить» и добавить вложения, поэтому я решил:

  1. Декомпиляция оригинального актера.

  2. Скопируйте логику оптом.

  3. Вставьте мои изменения, где это необходимо.


Подход

А Оригинальный актер:

А Модифицированный актер:

  • Строит а Multipart (“смешанный”) сообщение.

  • Добавляет исходное тело в качестве Текстовая частьПолем

  • Вызывает новый вспомогательный метод AddFileatTachments (Multipart)чтобы добавить загруженные файлы.

  • Решает загруженные файлы с Blobs CMS, определяет их тип контента и прикрепляет их к исходящей электронной почте.


Поддержка вложений

Обработка вложения включает в себя:

  • Обнаружение полей загрузки файла из подачи формы (FriendlyNameInfo и схемы именования поля, как файлВ загрузитьВ вложение)

  • Разрешение ссылок на Blob через Iblobfactory и IurlresolverПолем

  • Определение типа контента на основе расширения файла.

  • Внедрение вложений как Мимепарт Случаи с кодированием BASE64.


Дополнения зависимости

Чтобы сделать эту работу, я добавил:

  • IforMrepository – Получить метаданные поля формы.

  • Iblobfactory И Iurlresolver – Принесите бинарные данные для загруженных файлов.

  • А DetrineContentType Помощник – гарантирует, что вложения имеют правильные типы MIME.

Read more:  Почему важно преподавать образование в области изменения климата в раннем возрасте?

Изменения в строительстве тела

Оригинал
Использовал:

message.Body = new TextPart(subtype) { Text = this.RewriteUrls(content) };

Модифицированный
Окутает тело в Multipart (“смешанный”) Таким образом, он может содержать как тело, так и наложения:


Ключевые новые методы

Обработка вложения

protected virtual void AddFileAttachments(Multipart multipart)
{
    var friendlyNameInfos = _formRepository.Service.GetFriendlyNameInfos(this.FormIdentity);

    foreach (var submissionDataItem in this.SubmissionData.Data)
    {
        var friendlyNameInfo = friendlyNameInfos
            .FirstOrDefault(f => string.Equals(f.ElementId, submissionDataItem.Key, StringComparison.OrdinalIgnoreCase));

        if (friendlyNameInfo != null && IsFileUploadField(friendlyNameInfo, submissionDataItem.Value))
        {
            var fileUploads = submissionDataItem.Value.ToString().Split("|");

            foreach (var fileUpload in fileUploads)
            {
                AttachFileFromBlob(multipart, fileUpload, friendlyNameInfo.FriendlyName);
            }
        }
    }
}

Примечание: Если форма разрешает несколько загрузок, они проходят как одна строка, разделенная |Полем


Файл вложения помощника

protected virtual void AttachFileFromBlob(Multipart multipart, string blobIdentifier, string friendlyName)
{
    Blob blob = null;
    IContent contentData = _urlResolver.Service.Route(new UrlBuilder(blobIdentifier));

    if (contentData is MediaData media)
    {
        blob = _blobFactory.Service.GetBlob(media.BinaryData.ID);
    }

    if (blob != null)
    {
        var memoryStream = new MemoryStream();
        blob.OpenRead().CopyTo(memoryStream);
        memoryStream.Position = 0;

        var fileName = blobIdentifier.Split(new[] { "#@" }, StringSplitOptions.None).Last();
        var contentType = DetermineContentType(blob, fileName);

        multipart.Add(new MimePart(contentType)
        {
            Content = new MimeContent(memoryStream, ContentEncoding.Default),
            ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
            ContentTransferEncoding = ContentEncoding.Base64,
            FileName = fileName
        });
    }
}

Исходное имя файла добавляется к идентификатору после #@ – Это извлекает его для вложения по электронной почте.


Определить помощник типа контента

    protected virtual string DetermineContentType(Blob blob, string fileName)
    {
        // Try to get content type from blob
        // This might be available in blob metadata depending on your implementation

        // Fall back to determining from file extension
        string extension = Path.GetExtension(fileName)?.ToLowerInvariant();

        return extension switch
        {
            ".pdf" => "application/pdf",
            ".doc" => "application/msword",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".xls" => "application/vnd.ms-excel",
            ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".txt" => "text/plain",
            ".csv" => "text/csv",
            ".zip" => "application/zip",
            ".xml" => "application/xml",
            ".json" => "application/json",
            _ => "application/octet-stream"
        };
    }

SendMessage () Изменения

Оригинал:

MimeMessage message = new MimeMessage();
            
message.Subject = str1;
string subtype = this._sendMessageInHTMLFormat ? "html" : "plain";
message.Body = (MimeEntity) new TextPart(subtype, new object[1]
{
    (object) this.RewriteUrls(content)
});

Модифицировано:

MimeMessage message = new MimeMessage();
message.Subject = str1;

// Create multipart message to support attachments
var multipart = new Multipart("mixed");

string subtype = this._sendMessageInHTMLFormat ? "html" : "plain";
multipart.Add(new TextPart(subtype, new object[1]
{
       (object) this.RewriteUrls(content)
}));

// Add file attachments
this.AddFileAttachments(multipart);

message.Body = multipart;

Подключить его

Чтобы заменить актера вне коробки, вам, вероятно, потребуется сделать это в вашей настройке инъекции зависимостей.
В моем случае я не заменил его во всем мире – у нас уже был актер подклассов для применения различных шаблонов HTML, поэтому у меня было этот подкласс наследство от новая замена вместо оригинала.

Read more:  Авиадиспетчеры получают зарплату в размере 0 долларов, поскольку приостановка работы правительства затягивается | Закрытие федерального правительства США в 2025 году

Вынос

  • Оригинальный актер: Простая, надежная, нулевая поддержка вложения.

  • Модифицированный актер: Тот же фундамент, плюс многоуровневая MIME и обработка привязанности.

Если вам нужно расширить встроенные актеры в оптимизированных формах за пределы того, что предлагает публичный API, иногда вам приходится работать на уровне кода.

Осторожность: После любого обновления в пакете Forms проверьте, если SendEmailAftersUbmissionActor изменилась-вам может потребоваться повторно синхронизировать вашу версию или воспользоваться новыми основными функциями.

Есть какие -либо мысли или мнения? Дайте мне знать в комментариях!

11 августа 2025 года

Читайте также

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.