Заархивируйте 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 = "[email protected]";
    string to = "[email protected]";
    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-дерева с преобразованием xslt, поэтому я не хочу совершенно другого решение.

Есть предложения, почему файл zip поврежден?


person acraig5075    schedule 23.07.2012    source источник
comment
DeflateStream != Zip File (.zip может иметь несколько типов сжатия)   -  person user7116    schedule 23.07.2012
comment
возможный дубликат Как использовать System.IO. Сжатие для чтения / записи файлов ZIP?   -  person user7116    schedule 23.07.2012


Ответы (4)


Вы не создаете действительный zip-файл, вы просто создаете сжатый поток и записываете его в файл с расширением *.zip. Вместо DeflateStream следует использовать zip-библиотеку .NET.

Вы можете использовать что-то вроде библиотеки DotNetZip:

DotNetZip - это простая в использовании, БЫСТРАЯ, БЕСПЛАТНАЯ библиотека классов и набор инструментов для работы с zip-файлами или папками. Zip and Unzip - это просто: с DotNetZip приложения .NET, написанные на VB, C # - любом языке .NET - могут легко создавать, читать, извлекать или обновлять zip-файлы. Для Mono или MS .NET.

Если у вас есть ограничения и вы не можете использовать внешнюю библиотеку, вы можете попробовать использовать GZipStream и добавить вложение с расширением *.gz, которое будет поддерживаться общими инструментами сжатия.

В качестве полезной информации для справок в будущем .NET 4.5 наконец представит встроенную поддержку zip-архивирования через _ 5_ класс.

person João Angelo    schedule 23.07.2012

Я знаю, что это старый вопрос, но он возник, когда я искал то же самое.

Вот мое решение добавить сжатое (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);
}
person Niels R.    schedule 03.01.2014
comment
Спасибо за это. Однако мне пришлось установить ZipArchiveMode на Create. - person Foxhound; 17.05.2020

Для справки в будущем, альтернативный метод 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().

person acraig5075    schedule 24.07.2012

Благодаря ответу «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;
    }
}
person Rob    schedule 06.09.2017