C
C#6mo ago
Rvby

Writing Images to Zip File using DotNetZip

Hi, all! I'm working on a library that I plan to use for a much larger project, just to get my feet wet with it. Right now, I'm trying to take all images from a given folder and then push them into a .ZIP (really a .CBZ, but they're the same, just with a different extension). I want to avoid any disk IO until the .ZIP is fully created and ready to be written, though I am starting to wonder if this is possible without some temp files. I'm using ImageSharp to convert the files to .webp first, then trying to add them as entries into the .ZIP. Originally, I tried to use the built-in ZipArchive system. I was able to get it to write out a .CBZ with the images in there... or, to write files. The images would be corrupted during the write for some reason. My guess is that there's some weirdness around the stream being a compressed stream and my writing uncompressed data to it. I had a problem like this with it at work a while back. No matter--people seem to like DotNetZip, and it seemed simpler to use, so I decided to give that a try. My first attempt with it was this was this:
using (ZipFile zipFile = new ZipFile(zipPath))
{
foreach (string currentFilePath in imagePathsInFolder)
{
string currentFileName = Path.GetFileNameWithoutExtension(currentFilePath);
using (MemoryStream imageMemoryStream = new MemoryStream())
{
using (Image currentImage = Image.Load(currentFilePath))
{
currentImage.SaveAsWebp(imageMemoryStream, encoder);
}
ZipEntry entryStream = zipFile.AddEntry(currentFileName + ".webp", imageMemoryStream);
}
}
zipFile.Save();
}
using (ZipFile zipFile = new ZipFile(zipPath))
{
foreach (string currentFilePath in imagePathsInFolder)
{
string currentFileName = Path.GetFileNameWithoutExtension(currentFilePath);
using (MemoryStream imageMemoryStream = new MemoryStream())
{
using (Image currentImage = Image.Load(currentFilePath))
{
currentImage.SaveAsWebp(imageMemoryStream, encoder);
}
ZipEntry entryStream = zipFile.AddEntry(currentFileName + ".webp", imageMemoryStream);
}
}
zipFile.Save();
}
The above complains because it Cannot access a closed Stream. I'm guessing that the imageMemoryStreams aren't flushed properly into the zip until we choose to save to disk? I did try to push zipFile.save() up into the imageMemoryStream using statement, but that gives me a different headache around a NRE that must be happening in the internal code. I thought ZipOutputStream might be the right option instead, since it's purpose built to work with streams, but I'm getting a weird error when trying to run even the example code from the github:
string zipPath = Path.Combine(testFolder, "c01.cbz");

using (var fs = File.Create(zipPath))
{
using (var s = new ZipOutputStream(fs))
{
s.PutNextEntry("entry1.txt");
byte[] buffer = Encoding.ASCII.GetBytes("This is the content for entry #1.");
s.Write(buffer, 0, buffer.Length);
}
}
string zipPath = Path.Combine(testFolder, "c01.cbz");

using (var fs = File.Create(zipPath))
{
using (var s = new ZipOutputStream(fs))
{
s.PutNextEntry("entry1.txt");
byte[] buffer = Encoding.ASCII.GetBytes("This is the content for entry #1.");
s.Write(buffer, 0, buffer.Length);
}
}
'IBM437' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.
'IBM437' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.
StackOverflow directed me to this: https://stackoverflow.com/a/49068626 But even with that nuget package installed alongside a using directive and this line of code at the start of the test method, I'm still getting the above error. My project is in .NET 6.0. Any thoughts on what I'm doing wrong here? Or thoughts on how I could do this better?
Stack Overflow
'IBM437' is not a supported encoding name from ZipFile Read Method
I have a problem when my code execute this using: using (ZipFile archive = ZipFile.Read(File)) //<== Crash Here! { foreach (ZipEntry entry in archive.Entries) { entry.Extract(
7 Replies
Angius
Angius6mo ago
I tried building a zip archive entirely in-memory myself and gave up. Now I just create a directory in the temp folder, place files there, zip it up, then clean up the folder Much easier
Rvby
Rvby6mo ago
Yeah, I am leaning in that direction. Just a shame that there has to be disk IO involved before the end. I am going to go forward with the Disk IO route, but I do want to revisit this later, so I'm curious if anyone knows what's going on here and how I might get past it. Also happy to just steal/adapt another implementation someone has somewhere else. :P
canton7
canton76mo ago
The built-in zip stuff can do this fine. I've done it before In your code above you never rewound imageMemoryStream to the start. It's position is currently at the end of the data you wrote. You need .Position = 0; before giving it to the zoo stuff
mindhardt
mindhardt6mo ago
Lemme find an example I do create zip archives on the fly in wasm (no file io)
mindhardt
mindhardt6mo ago
It can give you a general idea All is done with built-in gzip
Rvby
Rvby6mo ago
This was the snafu! I'm honestly not super familiar with manipulating memory streams, so I didn't think about needing to do this. Thank you for this as well!