Большинство статей о добавлении вложений к оптимизированным формам электронных писем примерно с 2019 года и не охватывают самые последние версии. Этот пост проходит через мою реализацию 2025 года и извлеченные уроки. Ваш пробег может варьироваться.
Проблема
Оптимизируемые формы встроенные SendEmailAftersUbmissionActor Отправляет чистые, простые электронные письма после отправки формы. Когда вы используете Загрузка файла блок элемента элементаАктер включает в себя ссылки в электронном письме, где хранятся файлы в CMS.
Это хорошо для внутренних пользователей с CMS -доступом. Но если ваши получатели внешний – Клиенты, партнеры, поставщики – эти ссылки не будут работать для них.
Мне нужно было:
Почему подклассник не сработает
Оптимизируемые SendEmailAftersUbmissionActor только позволяет ограничить подкласс.
Многие из ключевых операций частныйнет защищени Mimemessage никогда не передается для настройки.
Это означает, что нет способа просто «зацепить» и добавить вложения, поэтому я решил:
-
Декомпиляция оригинального актера.
-
Скопируйте логику оптом.
-
Вставьте мои изменения, где это необходимо.
Подход
А Оригинальный актер:
А Модифицированный актер:
-
Строит а Multipart (“смешанный”) сообщение.
-
Добавляет исходное тело в качестве Текстовая частьПолем
-
Вызывает новый вспомогательный метод AddFileatTachments (Multipart)чтобы добавить загруженные файлы.
-
Решает загруженные файлы с Blobs CMS, определяет их тип контента и прикрепляет их к исходящей электронной почте.
Поддержка вложений
Обработка вложения включает в себя:
-
Обнаружение полей загрузки файла из подачи формы (FriendlyNameInfo и схемы именования поля, как файлВ загрузитьВ вложение)
-
Разрешение ссылок на Blob через Iblobfactory и IurlresolverПолем
-
Определение типа контента на основе расширения файла.
-
Внедрение вложений как Мимепарт Случаи с кодированием BASE64.
Дополнения зависимости
Чтобы сделать эту работу, я добавил:
-
IforMrepository – Получить метаданные поля формы.
-
Iblobfactory И Iurlresolver – Принесите бинарные данные для загруженных файлов.
-
А DetrineContentType Помощник – гарантирует, что вложения имеют правильные типы MIME.
Изменения в строительстве тела
Оригинал
Использовал:
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, поэтому у меня было этот подкласс наследство от новая замена вместо оригинала.
Вынос
-
Оригинальный актер: Простая, надежная, нулевая поддержка вложения.
-
Модифицированный актер: Тот же фундамент, плюс многоуровневая MIME и обработка привязанности.
Если вам нужно расширить встроенные актеры в оптимизированных формах за пределы того, что предлагает публичный API, иногда вам приходится работать на уровне кода.
Осторожность: После любого обновления в пакете Forms проверьте, если SendEmailAftersUbmissionActor изменилась-вам может потребоваться повторно синхронизировать вашу версию или воспользоваться новыми основными функциями.
Есть какие -либо мысли или мнения? Дайте мне знать в комментариях!
11 августа 2025 года
Читайте также
