Creating a ZIP archive in memory using System.IO.Compression
Thanks to ZipArchive creates invalid ZIP file, I got:
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!");
}
}
using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
That indicated we need to call Dispose
on ZipArchive
before we can use it, which as Amir suggests is likely because it writes final bytes like checksum to the archive that makes it complete. But in order not close the stream so we can re-use it after you need to pass true
as the third parameter to ZipArchive
.
How to create a zip file in memory, starting from file bytes?
Try to take a look to this: Creating a ZIP Archive in Memory Using System.IO.Compression. The solution is this:
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!");
}
}
using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
It explains how to create the zip archive in memory and contains a link to another useful article that explains the use of the leaveOpen argument to prevent the closing of the stream: ZipArchive creates invalid ZIP file that contains this solution:
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
var entry = zip.CreateEntry("test.txt");
using (StreamWriter sw = new StreamWriter(entry.Open()))
{
sw.WriteLine(
"Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
}
}
var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(
"test.zip",
CreationCollisionOption.ReplaceExisting);
zipStream.Position = 0;
using (Stream s = await file.OpenStreamForWriteAsync())
{
zipStream.CopyTo(s);
}
}
I hope it's helpful!
EDIT
Instead of zipArchiveMemoryStream.GetBuffer()
use zipArchiveMemoryStream.ToArray()
C# create zip file using zip archive System.IO.Compression
You don't close the stream
, you open with 'manifest.Open()
'. Then it might not have written everything to the zip.
Wrap it in another using
, like this:
using (var stream = new FileStream(path, FileMode.Create))
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry manifest = archive.CreateEntry(filenameManifest);
using (Stream st = manifest.Open())
{
using (StreamWriter writerManifest = new StreamWriter(st))
{
writerManifest.WriteLine(JSONObject_String);
}
}
ZipArchiveEntry pdfFile = archive.CreateEntry(filenameManifest);
using (Stream st = manifest.Open())
{
using (StreamWriter writerPDF = new StreamWriter(st))
{
writerPDF.WriteLine(pdf);
}
}
}
}
How to create ZipArchive from files in memory in C#?
Yes, you can do this, using the ZipArchive.CreateEntry method, as @AngeloReis pointed out in the comments, and described here for a slightly different problem.
Your code would then look like this:
using (var ms = new MemoryStream())
{
using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var attachment in attachmentFiles)
{
var entry = zipArchive.CreateEntry(attachment.FileName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
attachment.InputStream.CopyTo(entryStream);
}
}
}
...
}
C# ZipArchive - How to nest internal .zip files without writing to disk
So I created a FileStream instead of a MemoryStream so the code can be tested easier
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
}
You can obviously modify this method to return a MemoryStream, or change the method to Void to just have the zip file written out to disk
System.IO.Compression.ZipArchive memory management
The code you've written is causing the ZipArchive
class to write a whole new archive at the end of your previous one, which of course corrupts the file.
The way to do what you want is to copy the original archive to a new file as you create it, and then replace the original with the new one. For example:
string tempFile = Path.GetTempFileName();
using (ZipArchive original =
new ZipArchive(File.Open(archiveFileStream, FileMode.Open), ZipArchiveMode.Read))
using (ZipArchive newArchive =
new ZipArchive(File.Open(tempFile, FileMode.Create), ZipArchiveMode.Create))
{
foreach (ZipArchiveEntry entry in original.Entries)
{
ZipArchiveEntry newEntry = newArchive.Create(entry.FullName);
using (Stream source = entry.Open())
using (Stream destination = newEntry.Open())
{
source.CopyTo(destination);
}
}
for (int directoryGroupFileIndex = 0;
directoryGroupFileIndex < directoryGroup.Length;
directoryGroupFileIndex++)
{
FileInfo file = directoryGroup[directoryGroupFileIndex];
String archiveEntryName = file.Name;
String archiveEntryPath = DateTime.Now.ToString("yyyy-MM-dd");
String archiveEntryFullName = Path.Combine(archiveEntryPath, archiveEntryName);
ZipArchiveEntry archiveEntry = newArchive.CreateEntryFromFile(
file.FullName, archiveEntryFullName, CompressionLevel.Optimal);
}
}
File.Delete(archiveFileStream);
File.Move(tempFile, archiveFileStream);
Note that this isn't actually going to be slower than ZipArchiveMode.Update
. When you use the update mode, the ZipArchive
class reads the entire archive into memory (as you noted), and then when you close it, it recompresses and writes everything back out.
The above does basically the exact same computations, but simply uses the disk as the intermediate storage instead of memory.
Create Zip archive from multiple in memory files in C#
Use ZipEntry
and PutNextEntry()
for this. The following shows how to do it for a file, but for an in-memory object just use a MemoryStream
FileStream fZip = File.Create(compressedOutputFile);
ZipOutputStream zipOStream = new ZipOutputStream(fZip);
foreach (FileInfo fi in allfiles)
{
ZipEntry entry = new ZipEntry((fi.Name));
zipOStream.PutNextEntry(entry);
FileStream fs = File.OpenRead(fi.FullName);
try
{
byte[] transferBuffer[1024];
do
{
bytesRead = fs.Read(transferBuffer, 0, transferBuffer.Length);
zipOStream.Write(transferBuffer, 0, bytesRead);
}
while (bytesRead > 0);
}
finally
{
fs.Close();
}
}
zipOStream.Finish();
zipOStream.Close();
Zip files without inclusion of folders
You seem to be under the impression that the file will be added to the archive, which is not the case. CreateEntry
merely creates an entry with the specified path and entry name, you still need to write the actual file.
In fact, the code in your question is quite similar to the code in the documentation, so I assume you got it from there?
Anyway, to get only the file name you can use Path.GetFileName
and then you can write the actual file content to the zip entry.
var filePath = @"C:\temp\foo.txt";
var zipName = @"C:\temp\foo.zip";
using (FileStream zipToOpen = new FileStream(zipName, FileMode.CreateNew))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry(Path.GetFileName(filePath));
using (StreamReader reader = new StreamReader(filePath))
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.Write(reader.ReadToEnd());
}
}
}
The code above will create an archive with foo.txt
in the root and with the content of the source file, without any additional directories.
Related Topics
Kanji Characters from Webclient HTML Different from Actual Kanji in Website
Conversion of System.Array to List
Casting a Result to Float in Method Returning Float Changes Result
Use Own Icomparer<T> with Linq Orderby
Algorithm to Compare Two Images in C#
Converting Dd/Mm/Yyyy Formatted String to Datetime
How to Correctly Cast a Class to an Abstract Class When Using Type Generics
Get Controller and Action Name from Within Controller
Load Local HTML File in a C# Webbrowser
How to Filter All HTML Tags Except a Certain Whitelist
Unauthorised Webapi Call Returning Login Page Rather Than 401
Authorize by Group in Azure Active Directory B2C
When Do Static Variables Get Initialized in C#
Multi-Threaded Splash Screen in C#
ASP.NET Core 1.0 on Iis Error 502.5
How to Access Backing Fields Behind Auto-Implemented Properties