C # ZipArchive - Как вложить внутренние файлы .zip без записи на диск - PullRequest
3 голосов
/ 19 апреля 2019

Мне нужно создать zip-файл в памяти, а затем отправить zip-файл клиенту. Однако в некоторых случаях созданный zip-файл должен содержать другие zip-файлы, которые также были сгенерированы в памяти. Например, структура файла может выглядеть следующим образом:

 SendToClient.zip
   InnerZip1.zip
     File1.xml
     File2.xml
   InnerZip2.zip
     File3.xml
     File4.xml

Я пытался использовать библиотеку System.IO.Compression.ZipArchive. Я не могу использовать библиотеку System.IO.Compression.ZipFile, потому что версия моего проекта .NET не совместима с ней.

Вот пример того, что я пробовал.

public Stream GetMemoryStream() {
    var memoryStream = new MemoryStream();
    string fileContents = "Lorem ipsum dolor sit amet";
    string entryName = "Lorem.txt";
    string innerZipName = "InnerZip.zip";
    using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
        ZipArchiveEntry entry = archive.CreateEntry(Path.Combine(innerZipName, entryName), CompressionLevel.Optimal);
        using (var writer = new StreamWriter(entry.Open())) {
             writer.Write(fileContents);
        }
    }
    return memoryStream
}

Однако, это просто помещает Lorem.txt в папку с именем «Inner.zip» (вместо реального zip-файла).

Я могу создать пустой внутренний zip-файл, если я создаю запись с именем «Inner.zip» без записи в нее. Однако я ничего не могу добавить к нему, и запись в запись с именем «Inner.zip \ Lorem.txt» впоследствии просто снова создает папку (рядом с пустым .zip-файлом с тем же именем).

Я также попытался создать отдельный архив, сериализовать его с потоком памяти, а затем записать его в исходный архив в виде .zip.

public Stream CreateOuterZip() {
    var memoryStream = new MemoryStream();
    using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
        ZipArchiveEntry entry = archive.CreateEntry("Outer.zip", CompressionLevel.NoCompression);
        using (var writer = new BinaryWriter(entry.Open())) {
            writer.Write(GetMemoryStream().ToArray());
        }
    }
    return memoryStream;
}

Это просто создает недопустимый файл .zip, который, однако, Windows не знает, как его открыть.

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 19 апреля 2019

Вы должны также создать ZipArchive для внутреннего zip-файла. Запишите это в поток (поток памяти). И после записи этого потока как общий поток в основной почтовый индекс.

    static Stream Inner() {

      var memoryStream = new MemoryStream();
      using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {

        var demoFile = archive.CreateEntry("foo2.txt");

        using (var entryStream = demoFile.Open())
        using (var streamWriter = new StreamWriter(entryStream)) {
          streamWriter.Write("Bar2!");
        }
      }
      return memoryStream;
    }

    static void Main(string[] args) {

      using (var memoryStream = new MemoryStream()) {
        using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {

          var demoFile = archive.CreateEntry("foo.txt");

          using (var entryStream = demoFile.Open())
          using (var streamWriter = new StreamWriter(entryStream)) {
            streamWriter.Write("Bar!");
          }

          var zip = archive.CreateEntry("inner.zip");
          using (var entryStream = zip.Open()) {
            var inner = Inner();
            inner.Seek(0, SeekOrigin.Begin);
            inner.CopyTo(entryStream);
          }
        }

        using (var fileStream = new FileStream(@"d:\test.zip", FileMode.Create)) {
          memoryStream.Seek(0, SeekOrigin.Begin);
          memoryStream.CopyTo(fileStream);
        }
      }

Благодаря этому ответу.

2 голосов
/ 19 апреля 2019

Итак, я создал FileStream вместо MemoryStream, чтобы код можно было проще тестировать

public static Stream CreateOuterZip()
        {
            string fileContents = "Lorem ipsum dolor sit amet";

            // Final zip file
            var fs = new FileStream(
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SendToClient.zip"), FileMode.OpenOrCreate);

            // Create inner zip 1
            var innerZip1 = new MemoryStream();
            using (var archive = new ZipArchive(innerZip1, ZipArchiveMode.Create, true))
            {
                var file1 = archive.CreateEntry("File1.xml");
                using (var writer = new BinaryWriter(file1.Open()))
                {
                    writer.Write(fileContents); // Change fileContents to real XML content
                }
                var file2 = archive.CreateEntry("File2.xml");
                using (var writer = new BinaryWriter(file2.Open()))
                {
                    writer.Write(fileContents); // Change fileContents to real XML content
                }
            }

            // Create inner zip 2
            var innerZip2 = new MemoryStream();
            using (var archive = new ZipArchive(innerZip2, ZipArchiveMode.Create, true))
            {
                var file3 = archive.CreateEntry("File3.xml");
                using (var writer = new BinaryWriter(file3.Open()))
                {
                    writer.Write(fileContents); // Change fileContents to real XML content
                }
                var file4 = archive.CreateEntry("File4.xml");
                using (var writer = new BinaryWriter(file4.Open()))
                {
                    writer.Write(fileContents); // Change fileContents to real XML content
                }
            }

            using (var archive = new ZipArchive(fs, ZipArchiveMode.Create, true))
            {
                // Create inner zip 1
                var innerZipEntry = archive.CreateEntry("InnerZip1.zip");
                innerZip1.Position = 0;
                using (var s = innerZipEntry.Open())
                {
                    innerZip1.WriteTo(s);
                }

                // Create inner zip 2
                var innerZipEntry2 = archive.CreateEntry("InnerZip2.zip");
                innerZip2.Position = 0;
                using (var s = innerZipEntry2.Open())
                {
                    innerZip2.WriteTo(s);
                }
            }

            fs.Close();
            return fs; // The file is written, can probably just close this
        }

Очевидно, что вы можете изменить этот метод, чтобы он возвращал MemoryStream, или изменить метод на Void, чтобы получить только zipфайл записан на диск

...