I tried following this SO: Create zip file from byte[] as a dummy project and it looks like this:
using System.IO.Compression;
using System.IO;
using System.Net.Http;
using System;
namespace TestApp
{
internal class Program
{
static void Main(string[] args)
{
using var compressedFileStream = new MemoryStream();
using var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create);
//Create a zip entry for each attachment
var zipEntry = zipArchive.CreateEntry("test.txt");
var file = File.ReadAllBytes("test.txt");
//Get the stream of the attachment
using var originalFileStream = new MemoryStream(file);
using var zipEntryStream = zipEntry.Open();
//Copy the attachment stream to the zip entry stream
originalFileStream.CopyTo(zipEntryStream);
var toarraybaby = compressedFileStream.ToArray();
File.WriteAllBytes("hehe.zip", toarraybaby);
}
}
}
I get a .zip file as output and the file has a size. But when trying to open the file I get that its corrupt. What am I missing?
CodePudding user response:
What's wrong with that code specifically is that a C# 8-style using
declaration disposes the at end of the current scope. That's too late. The zip archive has to be disposed to ensure it writes all the necessary data to its output stream, but by the time it does that, Main
has ended.
There are various ways to ensure it gets disposed earlier, for example:
using var compressedFileStream = new MemoryStream();
// using an old-style using statement here to ensure the zip archive gets disposed early enough
using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create))
{
//Create a zip entry for each attachment
var zipEntry = zipArchive.CreateEntry("test.txt");
var file = File.ReadAllBytes("test.txt");
//Get the stream of the attachment
using var originalFileStream = new MemoryStream(file);
using var zipEntryStream = zipEntry.Open();
//Copy the attachment stream to the zip entry stream
originalFileStream.CopyTo(zipEntryStream);
}
var toarraybaby = compressedFileStream.ToArray();
File.WriteAllBytes("hehe.zip", toarraybaby);
CodePudding user response:
I just used the following code and it worked for me:
var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var outputFilePath = Path.Combine(folderPath, "Test.zip");
using var outputFile = File.Create(outputFilePath);
using var archive = new ZipArchive(outputFile, ZipArchiveMode.Create);
var inputFileName = "Test.txt";
var inputFilePath = Path.Combine(folderPath, inputFileName);
var entry = archive.CreateEntry(inputFileName);
using var inputFile = File.OpenRead(inputFilePath);
using var entryStream = entry.Open();
inputFile.CopyTo(entryStream);
I was able to open that ZIP file in File Explorer and then open the text file it contained and see the same original text. If you want to create a new ZIP file containing arbitrary existing files, that's how I'd do it.
I then tried replacing the output FileStream
with a MemoryStream
while making the fewest changes to the code as possible and got this:
var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var outputFilePath = Path.Combine(folderPath, "Test.zip");
using var outputStream = new MemoryStream();
using var archive = new ZipArchive(outputStream, ZipArchiveMode.Create);
var inputFileName = "Test.txt";
var inputFilePath = Path.Combine(folderPath, inputFileName);
var entry = archive.CreateEntry(inputFileName);
using var inputFile = File.OpenRead(inputFilePath);
using var entryStream = entry.Open();
inputFile.CopyTo(entryStream);
var data = outputStream.ToArray();
File.WriteAllBytes(outputFilePath, data);
When I ran that, I ended up with a corrupt ZIP file. I'm not sure what the specific reason is but, if my first code snippet works for you, it's not really worth finding out. If you really need to use a MemoryStream
, let me know and I'll investigate a bit further. It may have to do with the order of disposing objects but I'm not sure.
CodePudding user response:
The problem is that you are not flushing the Zip into the MemoryStream
until it gets closed at the end of the function. You could manually put in a Flush
or close the using
at the right place.
But ideally you would not use a MemoryStream
at all. Instead, just feed the zip straight into a FileStream
. Also you should use async
if possible.
static async Task Main(string[] args)
{
using var compressedFileStream = new FileStream("hehe.zip", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, true);
using var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create);
//Create a zip entry for each attachment
var zipEntry = zipArchive.CreateEntry("test.txt");
using var originalFileStream = new FileStream("test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.None, 4096, true);
//Get the stream of the attachment
using var zipEntryStream = zipEntry.Open();
//Copy the attachment stream to the zip entry stream
await originalFileStream.CopyToAsync(zipEntryStream);
}