Заархивируйте MemoryStream для вложения электронной почты
Я пытаюсь сжать дерево XML и использовать его в качестве вложения электронной почты. Отправка электронного письма с вложением завершается успешно, но созданный zip-файл всегда поврежден - он не является действительным zip-файлом, но содержит двоичные данные.
Проблема воссоздается следующим образом, смотрите конкретно BuildAttachment()
:
static void Main(string[] args)
{
try
{
var report = new XElement("Report",
new XElement("Product",
new XElement("ID", "10000001"),
new XElement("Name", "abcdefghijklm"),
new XElement("Group", "nopqrstuvwxyz")
)
);
var mailMessage = BuildMessage(report);
EmailMessage(mailMessage);
Thread.Sleep(10000);
}
catch (Exception e) { Console.WriteLine(e.Message); }
}
static MailMessage BuildMessage(XElement report)
{
string from = "email1@address.com";
string to = "email2@address.com";
var message = new MailMessage(from, to, "Subject text", "Body text");
var attachment = BuildAttachment(report);
message.Attachments.Add(attachment);
return message;
}
static Attachment BuildAttachment(XElement report)
{
var inStream = new MemoryStream();
report.Save(inStream);
inStream.Position = 0;
var outStream = new MemoryStream();
var compress = new DeflateStream(outStream, CompressionMode.Compress);
inStream.CopyTo(compress);
outStream.Position = 0;
return new Attachment(outStream, "report.zip", "application/zip");
}
static void EmailMessage(MailMessage message)
{
var smtpClient = new SmtpClient("127.0.0.1");
smtpClient.SendCompleted += SendCompletedCallback;
smtpClient.SendAsync(message, null);
}
static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
Console.WriteLine(e.Error.ToString());
}
Чтобы поставить проблему в контекст: это часть приложения-службы Windows, поэтому я не хочу создавать файлы на диске, и сообщение электронной почты также содержит xslt-преобразованные альтернативные представления дерева xml, поэтому я не хочу совершенно другого решение.
Любые предложения, почему почтовый файл поврежден?
4 ответа
Вы не создаете действительный zip-файл, вы просто создаете сжатый поток и записываете его в файл, который имеет *.zip
расширение. Вы должны использовать.NET zip-библиотеку вместо DeflateStream
,
Вы можете использовать что-то вроде библиотеки DotNetZip:
DotNetZip - это простая в использовании, БЫСТРАЯ, БЕСПЛАТНАЯ библиотека классов и набор инструментов для работы с zip-файлами или папками. Zip и Unzip просты: с помощью DotNetZip приложения.NET, написанные на VB, C# - на любом языке.NET - могут легко создавать, читать, извлекать или обновлять zip-файлы. Для моно или MS .NET.
Если у вас есть ограничения и вы не можете использовать внешнюю библиотеку, вы можете попробовать использовать GZipStream
и добавить вложение с расширением *.gz
который будет поддерживаться общими инструментами сжатия.
В качестве полезной информации для дальнейшего использования.NET 4.5 наконец-то представит встроенную поддержку zip-архивирования через ZipArchive
учебный класс.
Я знаю, что это старый вопрос, но он появился, когда я искал то же самое.
Вот мое решение добавить сжатое (zip) вложение, используя System.IO.Compression.ZipArchive (требуется.NET 4.5 или выше) [на основе ответа acraig5075]:
byte[] report = GetSomeReportAsByteArray();
string fileName = "file.txt";
using (MemoryStream memoryStream = new MemoryStream())
{
using (ZipArchive zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Update))
{
ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry(fileName);
using (StreamWriter streamWriter = new StreamWriter(zipArchiveEntry.Open()))
{
streamWriter.Write(Encoding.Default.GetString(report));
}
}
MemoryStream attachmentStream = new MemoryStream(memoryStream.ToArray());
Attachment attachment = new Attachment(attachmentStream, fileName + ".zip", MediaTypeNames.Application.Zip);
mail.Attachments.Add(attachment);
}
Для дальнейшего использования альтернативный GZipStream
Метод, предложенный в принятом ответе, для создания архива.gz достигается путем изменения приведенного выше кода следующим образом.
static Attachment BuildAttachment(XElement report)
{
var inStream = new MemoryStream();
report.Save(inStream);
inStream.Position = 0;
var outStream = new MemoryStream();
using (var compress = new GZipStream(outStream, CompressionMode.Compress))
{
inStream.CopyTo(compress);
compress.Close();
}
var ms = new MemoryStream(outStream.ToArray());
Attachment attachment = new Attachment(ms, "report.xml.gz", "application/gzip");
return attachment;
}
using
блок и ToArray()
Требуется звонок.
Благодаря ответу "Neils. R", я создал статическую функцию C# для управления вложениями и архивирования их, если их размер превышает 1 МБ. Надеюсь, это кому-нибудь поможет.
public static Attachment CreateAttachment(string fileNameAndPath, bool zipIfTooLarge = true, int bytes = 1 << 20)
{
if (!zipIfTooLarge)
{
return new Attachment(fileNameAndPath);
}
var fileInfo = new FileInfo(fileNameAndPath);
// Less than 1Mb just attach as is.
if (fileInfo.Length < bytes)
{
var attachment = new Attachment(fileNameAndPath);
return attachment;
}
byte[] fileBytes = File.ReadAllBytes(fileNameAndPath);
using (var memoryStream = new MemoryStream())
{
string fileName = Path.GetFileName(fileNameAndPath);
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create))
{
ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var streamWriter = new StreamWriter(zipArchiveEntry.Open()))
{
streamWriter.Write(Encoding.Default.GetString(fileBytes));
}
}
var attachmentStream = new MemoryStream(memoryStream.ToArray());
string zipname = $"{Path.GetFileNameWithoutExtension(fileName)}.zip";
var attachment = new Attachment(attachmentStream, zipname, MediaTypeNames.Application.Zip);
return attachment;
}
}