Invalid zip file reading from a Stream in C# - c#

I have the following code:
private static byte[] ConverterStringToByte(Stream body)
{
string fileName = "data_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
// Take out the bytes from the memory stream and safely close the stream
using (var ms = new MemoryStream())
{
body.CopyTo(ms);
using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, false))
{
var zipEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
using (BinaryWriter writer = new BinaryWriter(zipEntry.Open()))
{
ms.Position = 0;
writer.Write(ms.ToArray());
}
}
return ms.ToArray();
}
}
I am downloading the file successfully, however I'm getting
invalid file
when trying to open

I think it should be something like this. Not sure about fileName though, because it's the name of the file being put into archive, so I don't think it should have *.zip extension. Unless you are creating a zip of zips.
static byte[] ConverterStringToByte(Stream body)
{
string fileName = #"data_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var ms = new MemoryStream())
{
using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, false))
{
var zipEntry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var destStream = zipEntry.Open())
{
body.CopyTo(destStream);
}
}
return ms.ToArray();
}
}

Related

Copy files from one Zip file to another

I am copying files from one zip file to another in certain circumstances. I am wondering if there is a better way to do it than what I came up with:
using FileStream sourceFileStream = new FileStream(source.FileName, FileMode.Open);
using FileStream targetFileStream = new FileStream(target.FileName, FileMode.Open, FileAccess.ReadWrite);
using ZipArchive sourceZip = new ZipArchive(sourceFileStream, ZipArchiveMode.Read);
using ZipArchive targetZip = new ZipArchive(targetFileStream, ZipArchiveMode.Update);
ZipArchiveEntry sourceEntry = sourceZip.GetEntry(filePathInArchive);
if (sourceEntry == null)
return;
ZipArchiveEntry targetEntry = targetZip.GetEntry(filePathInArchive);
if (targetEntry != null)
targetEntry.Delete();
targetZip.CreateEntry(filePathInArchive);
targetEntry = targetZip.GetEntry(filePathInArchive);
if (targetEntry != null)
{
Stream writer = targetEntry.Open();
Stream reader = sourceEntry.Open();
int b;
do
{
b = reader.ReadByte();
writer.WriteByte((byte)b);
} while (b != -1);
writer.Close();
reader.Close();
}
Tips and suggestions would be appreciated.
You can iterate each entry from source archive with opening its streams and using Stream.CopyTo write source entry content to target entry.
From C# 8.0 it looks compact and works fine:
static void CopyZipEntries(string sourceZipFile, string targetZipFile)
{
using FileStream sourceFS = new FileStream(sourceZipFile, FileMode.Open);
using FileStream targetFS = new FileStream(targetZipFile, FileMode.Open);
using ZipArchive sourceZIP = new ZipArchive(sourceFS, ZipArchiveMode.Read, false, Encoding.GetEncoding(1251));
using ZipArchive targetZIP = new ZipArchive(targetFS, ZipArchiveMode.Update, false, Encoding.GetEncoding(1251));
foreach (ZipArchiveEntry sourceEntry in sourceZIP.Entries)
{
// 'is' is replacement for 'null' check
if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
existingTargetEntry.Delete();
using (Stream targetEntryStream = targetZIP.CreateEntry(sourceEntry.FullName).Open())
{
sourceEntry.Open().CopyTo(targetEntryStream);
}
}
}
With earlier than C# 8.0 versions it works fine too, but more braces needed:
static void CopyZipEntries(string sourceZipFile, string targetZipFile)
{
using (FileStream sourceFS = new FileStream(sourceZipFile, FileMode.Open))
{
using (FileStream targetFS = new FileStream(targetZipFile, FileMode.Open))
{
using (ZipArchive sourceZIP = new ZipArchive(sourceFS, ZipArchiveMode.Read, false, Encoding.GetEncoding(1251)))
{
using (ZipArchive targetZIP = new ZipArchive(targetFS, ZipArchiveMode.Update, false, Encoding.GetEncoding(1251)))
{
foreach (ZipArchiveEntry sourceEntry in sourceZIP.Entries)
{
if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
{
existingTargetEntry.Delete();
}
using (Stream target = targetZIP.CreateEntry(sourceEntry.FullName).Open())
{
sourceEntry.Open().CopyTo(target);
}
}
}
}
}
}
}
For single specified file copy just replace bottom part from foreach loop to if condition:
static void CopyZipEntry(string fileName, string sourceZipFile, string targetZipFile)
{
// ...
// It means specified file exists in source ZIP-archive
// and we can copy it to target ZIP-archive
if (sourceZIP.GetEntry(fileName) is ZipArchiveEntry sourceEntry)
{
if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
existingTargetEntry.Delete();
using (Stream targetEntryStream = targetZIP.CreateEntry(sourceEntry.FullName).Open())
{
sourceEntry.Open().CopyTo(targetEntryStream);
}
}
else
MessageBox.Show("Source ZIP-archive doesn't contains file " + fileName);
}
Thanks to the input so far, I cleaned up and improved the code. I think this looks cleaner and more reliable.
//Making sure files exist etc before this part...
string filePathInArchive = source.GetFilePath(fileId);
using FileStream sourceFileStream = new FileStream(source.FileName, FileMode.Open);
using FileStream targetFileStream = new FileStream(target.FileName, FileMode.Open, FileAccess.ReadWrite);
using ZipArchive sourceZip = new ZipArchive(sourceFileStream, ZipArchiveMode.Read, false );
using ZipArchive targetZip = new ZipArchive(targetFileStream, ZipArchiveMode.Update, false);
ZipArchiveEntry sourceEntry = sourceZip.GetEntry(filePathInArchive);
if (sourceEntry != null)
{
if (targetZip.GetEntry(filePathInArchive) is { } existingTargetEntry)
{
existingTargetEntry.Delete();
}
using var targetEntryStream = targetZip.CreateEntry(sourceEntry.FullName).Open();
sourceEntry.Open().CopyTo(targetEntryStream);
}

C# ZipArchive - How to nest internal .zip files without writing to disk

I need to create a zip file in memory, then send the zip file to the client. However, there are cases where the created zip file will need to contain other zip files that were also generated in memory. For instance, the file structure might look like this:
SendToClient.zip
InnerZip1.zip
File1.xml
File2.xml
InnerZip2.zip
File3.xml
File4.xml
I've been attempting to use the System.IO.Compression.ZipArchive library. I cannot use the System.IO.Compression.ZipFile library because my project's version of .NET is not compatible with it.
Here's an example of what I've tried.
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
}
However, this just puts Lorem.txt in a folder called "Inner.zip" (instead of in an actual zip file).
I can create an empty inner zip file if I create an entry called "Inner.zip" without writing to it. I can't add anything to it, though, and writing to an entry called "Inner.zip\Lorem.txt" afterward just creates a folder again (alongside the identically named empty .zip file).
I've also tried creating a separate archive, serializing it with a memory stream, then writing that to the original archive as a .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;
}
This just creates an invalid .zip file that windows doesn't know how to open, though.
Thanks in advance!
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
You should create ZipArchive for internal zip file also. Write it to stream (memorystream). And after write this stream as general stream into main 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);
}
}
Thanks to this answer.

Getting extra space in last line after file seek and zipping it

Using below code I am using File seek and convert to result byte to compressed stream and generating the zip file,
public static async Task Get(string filename)
{
byte[] result;
byte[] compressedBytes;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
SourceStream.Seek(20, SeekOrigin.Begin);
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
string fileName = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = File.Create(fileName))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(result))
{
fileToCompressStream.CopyTo(entryStream);
}
}
}
}
Now when I unzip the resultant file has extra space. What's the reason for it and how to resolve it?
You're seeking 20 bytes into the stream, but the length of your array is the complete length of the stream. Therefore the final 20 bytes in your array are being ignored.
The simple fix for this is just to allocate less space, and then only ask to read the reduced number of bytes:
result = new byte[SourceStream.Length - 20];
await SourceStream.ReadAsync(result, 0, result.Length);
Note that you're also assuming that a single call to ReadAsync will read all the data. That may be the case in many situations, but it's generally not a good idea to assume that about streams.
It would be simpler just to copy straight from the file stream to the compressed stream though, instead of reading the whole file into memory first:
public static async Task Get(string filename)
{
string outputFile = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = File.Create(outputFile))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = File.Open(filename, FileMode.Open))
{
// Skip the first 20 bytes
fileToCompressStream.Position = 20;
fileToCompressStream.CopyTo(entryStream);
}
}
}
}

Invalid zip file after creating it with System.IO.Compression

I'm trying to create a zip file that contains one or more files.
I'm using the .NET framework 4.5 and more specifically System.IO.Compression namespace.
The objective is to allow a user to download a zip file through a ASP.NET MVC application.
The zip file is being generated and sent to the client but when I try to open it by doing double click on it I get the following error:
Windows cannot open the folder.
The compressed (zipped) folder ... is invalid.
Here's my code:
[HttpGet]
public FileResult Download()
{
var fileOne = CreateFile(VegieType.POTATO);
var fileTwo = CreateFile(VegieType.ONION);
var fileThree = CreateFile(VegieType.CARROT);
IEnumerable<FileContentResult> files = new List<FileContentResult>() { fileOne, fileTwo, fileThree };
var zip = CreateZip(files);
return zip;
}
private FileContentResult CreateFile(VegieType vType)
{
string fileName = string.Empty;
string fileContent = string.Empty;
switch (vType)
{
case VegieType.BATATA:
fileName = "batata.csv";
fileContent = "THIS,IS,A,POTATO";
break;
case VegieType.CEBOLA:
fileName = "cebola.csv";
fileContent = "THIS,IS,AN,ONION";
break;
case VegieType.CENOURA:
fileName = "cenoura.csv";
fileContent = "THIS,IS,A,CARROT";
break;
default:
break;
}
var fileBytes = Encoding.GetEncoding(1252).GetBytes(fileContent);
return File(fileBytes, MediaTypeNames.Application.Octet, fileName);
}
private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
byte[] retVal = null;
if (files.Any())
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
entryStream.Write(f.FileContents, 0, f.FileContents.Length);
entryStream.Close();
}
}
zipStream.Position = 0;
retVal = zipStream.ToArray();
}
}
}
return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}
Can anyone please shed some light on why is windows saying that my zip file is invalid when I double click on it.
A final consideration, I can open it using 7-Zip.
You need to get the MemoryStream buffer via ToArray after the ZipArchive object gets disposed. Otherwise you end up with corrupted archive.
And please note that I have changed the parameters of ZipArchive constructor to keep it open when adding entries.
There is some checksumming going on when the ZipArchive is beeing disposed so if you read the MemoryStream before, it is still incomplete.
private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
byte[] retVal = null;
if (files.Any())
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (BinaryWriter writer = new BinaryWriter(entry.Open()))
{
writer.Write(f.FileContents, 0, f.FileContents.Length);
writer.Close();
}
}
zipStream.Position = 0;
}
retVal = zipStream.ToArray();
}
}
return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}
Just return the stream...
private ActionResult CreateZip(IEnumerable files)
{
if (files.Any())
{
MemoryStream zipStream = new MemoryStream();
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
entryStream.Write(f.FileContents, 0, f.FileContents.Length);
entryStream.Close();
}
}
}
zipStream.Position = 0;
return File(zipStream, MediaTypeNames.Application.Zip, "horta.zip");
}
return new EmptyResult();
}
Try changing
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
to
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
In this usage, the archive is forced to write to the stream when it is closed. However, if the leaveOpen argument of the constructor is set to false, it will close the underlying stream too.
When I added a wrong name for the entry as in the example
var fileToZip = "/abc.txt";
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip);
I got the same error. After correcting the file name, it is ok now.
I got the "The compressed (zipped) folder ... is invalid." error because my entries were named with a leading "/" in front of them. Some zip extractors had no problem with this but the Windows one does. I resolved it by removing the slash from the entry name (from "/file.txt" to "file.txt").

Create zip as byte[] in memory without saving to disk? [duplicate]

I'm trying to create a ZIP archive with a simple demo text file using a MemoryStream as follows:
using (var memoryStream = new MemoryStream())
using (var archive = new ZipArchive(memoryStream , ZipArchiveMode.Create))
{
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))
{
stream.CopyTo(fileStream);
}
}
If I run this code, the archive file itself is created but foo.txt isn't.
However, if I replace the MemoryStream directly with the file stream, the archive is created correctly:
using (var fileStream = new FileStream(#"C:\Temp\test.zip", FileMode.Create))
using (var archive = new ZipArchive(fileStream, FileMode.Create))
{
// ...
}
Is it possible to use a MemoryStream to create the ZIP archive without the FileStream?
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.
Just another version of zipping without writing any file.
string fileName = "export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx";
byte[] fileBytes = here is your file in bytes
byte[] compressedBytes;
string fileNameZip = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(fileBytes))
{
fileToCompressStream.CopyTo(entryStream);
}
}
compressedBytes = outStream.ToArray();
}
Set the position of the stream to the 0 before copying it to the zip stream.
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.Position=0;
memoryStream.WriteTo(fileStream);
}
}
Working solution for MVC
public ActionResult Index()
{
string fileName = "test.pdf";
string fileName1 = "test.vsix";
string fileNameZip = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
byte[] fileBytes = System.IO.File.ReadAllBytes(#"C:\test\test.pdf");
byte[] fileBytes1 = System.IO.File.ReadAllBytes(#"C:\test\test.vsix");
byte[] compressedBytes;
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(fileBytes))
{
fileToCompressStream.CopyTo(entryStream);
}
var fileInArchive1 = archive.CreateEntry(fileName1, CompressionLevel.Optimal);
using (var entryStream = fileInArchive1.Open())
using (var fileToCompressStream = new MemoryStream(fileBytes1))
{
fileToCompressStream.CopyTo(entryStream);
}
}
compressedBytes = outStream.ToArray();
}
return File(compressedBytes, "application/zip", fileNameZip);
}
You need to finish writing the memory stream then read the buffer back.
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create))
{
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))
{
var bytes = memoryStream.GetBuffer();
fileStream.Write(bytes,0,bytes.Length );
}
}
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program`enter code here`
{
static void Main(string[] args)
{
using (FileStream zipToOpen = new FileStream(#"c:\users\exampleuser\release.zip", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
}
}
}
}
Function to return stream that contain zip file
public static Stream ZipGenerator(List<string> files)
{
ZipArchiveEntry fileInArchive;
Stream entryStream;
int i = 0;
List<byte[]> byteArray = new List<byte[]>();
foreach (var file in files)
{
byteArray.Add(File.ReadAllBytes(file));
}
var outStream = new MemoryStream();
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
fileInArchive=(archive.CreateEntry(Path.GetFileName(file), CompressionLevel.Optimal));
using (entryStream = fileInArchive.Open())
{
using (var fileToCompressStream = new MemoryStream(byteArray[i]))
{
fileToCompressStream.CopyTo(entryStream);
}
i++;
}
}
}
outStream.Position = 0;
return outStream;
}
If you want , write zip to file stream.
using (var fileStream = new FileStream(#"D:\Tools\DBExtractor\DBExtractor\bin\Debug\test.zip", FileMode.Create))
{
outStream.Position = 0;
outStream.WriteTo(fileStream);
}
`
I'm late to the party, but there are scenarios where you can't access the ZipArchive's constructor to set the leaveOpen parameter and where you don't want the ZIP to be written to disk. In my case, the AsiceArchive class I'm using internally creates a ZipArchive but doesn't set leaveOpen to true.
I created a subclass of Stream that delegates all calls to an inner stream (a few clicks with ReSharper). This class is not disposable, so when the ZipArchive gets disposed, nothing happens to the inner stream.
public class NondisposingStreamWrapper : Stream
{
private readonly Stream _streamImplementation;
public NondisposingStreamWrapper(Stream inner) => _streamImplementation = inner;
public override void Flush() => _streamImplementation.Flush();
public override int Read(byte[] buffer, int offset, int count) => _streamImplementation.Read(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => _streamImplementation.Seek(offset, origin);
public override void SetLength(long value) => _streamImplementation.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => _streamImplementation.Write(buffer, offset, count);
public override bool CanRead => _streamImplementation.CanRead;
public override bool CanSeek => _streamImplementation.CanSeek;
public override bool CanWrite => _streamImplementation.CanWrite;
public override long Length => _streamImplementation.Length;
public override long Position
{
get => _streamImplementation.Position;
set => _streamImplementation.Position = value;
}
}
Use it like this:
using var memoryStream = new MemoryStream();
var output = new NondisposingStreamWrapper(memoryStream);
using (var archive = new ZipArchive(output, ZipArchiveMode.Create))
{
// add entries to archive
}
memoryStream.Flush();
memoryStream.Position = 0;
// write to file just for testing purposes
File.WriteAllBytes("out.zip", memoryStream.ToArray());
This is the way to convert a entity to XML File and then compress it:
private void downloadFile(EntityXML xml) {
string nameDownloadXml = "File_1.xml";
string nameDownloadZip = "File_1.zip";
var serializer = new XmlSerializer(typeof(EntityXML));
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AddHeader("content-disposition", "attachment;filename=" + nameDownloadZip);
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry(nameDownloadXml);
using (var entryStream = demoFile.Open())
using (StreamWriter writer = new StreamWriter(entryStream, System.Text.Encoding.UTF8))
{
serializer.Serialize(writer, xml);
}
}
using (var fileStream = Response.OutputStream)
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
Response.End();
}
Just in case, if anyone wants to save a dynamic zip file through SaveFileDialog.
var logFileName = "zip_filename.zip";
appLogSaver.FileName = logFileName;
appLogSaver.Filter = "LogFiles|*.zip";
appLogSaver.DefaultExt = "zip";
DialogResult resDialog = appLogSaver.ShowDialog();
if (resDialog.ToString() == "OK")
{
System.IO.FileStream fs = (System.IO.FileStream)appLogSaver.OpenFile();
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))
{
//read your existing file and put the content here
streamWriter.Write("Bar!");
}
}
var demoFile2 = archive.CreateEntry("foo2.txt");
using (var entryStream = demoFile2.Open())
{
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("Bar2!");
}
}
}
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fs);
}
fs.Close();
}
For me something like this was ok:
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var file = archive.CreateEntry("file.json");
using var entryStream = file.Open();
using var streamWriter = new StreamWriter(entryStream);
streamWriter.WriteLine(someJsonLine);
}
}
private void button6_Click(object sender, EventArgs e)
{
//create With Input FileNames
AddFileToArchive_InputByte(new ZipItem[]{ new ZipItem( #"E:\b\1.jpg",#"images\1.jpg"),
new ZipItem(#"E:\b\2.txt",#"text\2.txt")}, #"C:\test.zip");
//create with input stream
AddFileToArchive_InputByte(new ZipItem[]{ new ZipItem(File.ReadAllBytes( #"E:\b\1.jpg"),#"images\1.jpg"),
new ZipItem(File.ReadAllBytes(#"E:\b\2.txt"),#"text\2.txt")}, #"C:\test.zip");
//Create Archive And Return StreamZipFile
MemoryStream GetStreamZipFile = AddFileToArchive(new ZipItem[]{ new ZipItem( #"E:\b\1.jpg",#"images\1.jpg"),
new ZipItem(#"E:\b\2.txt",#"text\2.txt")});
//Extract in memory
ZipItem[] ListitemsWithBytes = ExtractItems(#"C:\test.zip");
//Choese Files For Extract To memory
List<string> ListFileNameForExtract = new List<string>(new string[] { #"images\1.jpg", #"text\2.txt" });
ListitemsWithBytes = ExtractItems(#"C:\test.zip", ListFileNameForExtract);
// Choese Files For Extract To Directory
ExtractItems(#"C:\test.zip", ListFileNameForExtract, "c:\\extractFiles");
}
public struct ZipItem
{
string _FileNameSource;
string _PathinArchive;
byte[] _Bytes;
public ZipItem(string __FileNameSource, string __PathinArchive)
{
_Bytes=null ;
_FileNameSource = __FileNameSource;
_PathinArchive = __PathinArchive;
}
public ZipItem(byte[] __Bytes, string __PathinArchive)
{
_Bytes = __Bytes;
_FileNameSource = "";
_PathinArchive = __PathinArchive;
}
public string FileNameSource
{
set
{
FileNameSource = value;
}
get
{
return _FileNameSource;
}
}
public string PathinArchive
{
set
{
_PathinArchive = value;
}
get
{
return _PathinArchive;
}
}
public byte[] Bytes
{
set
{
_Bytes = value;
}
get
{
return _Bytes;
}
}
}
public void AddFileToArchive(ZipItem[] ZipItems, string SeveToFile)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0;
while (fsReader.Position != fsReader.Length)
{
//Read Bytes
ReadByte = fsReader.Read(ReadAllbytes, 0, ReadAllbytes.Length);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
}
fsReader.Dispose();
OpenFileInArchive.Close();
}
archive.Dispose();
using (var fileStream = new FileStream(SeveToFile, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
public MemoryStream AddFileToArchive(ZipItem[] ZipItems)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0;
while (fsReader.Position != fsReader.Length)
{
//Read Bytes
ReadByte = fsReader.Read(ReadAllbytes, 0, ReadAllbytes.Length);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
}
fsReader.Dispose();
OpenFileInArchive.Close();
}
archive.Dispose();
return memoryStream;
}
public void AddFileToArchive_InputByte(ZipItem[] ZipItems, string SeveToFile)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
// FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 4096 ;int TotalWrite=0;
while (TotalWrite != item.Bytes.Length)
{
if(TotalWrite+4096>item.Bytes.Length)
ReadByte=item.Bytes.Length-TotalWrite;
Array.Copy(item.Bytes, TotalWrite, ReadAllbytes, 0, ReadByte);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
TotalWrite += ReadByte;
}
OpenFileInArchive.Close();
}
archive.Dispose();
using (var fileStream = new FileStream(SeveToFile, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
public MemoryStream AddFileToArchive_InputByte(ZipItem[] ZipItems)
{
MemoryStream memoryStream = new MemoryStream();
//Create Empty Archive
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (ZipItem item in ZipItems)
{
//Create Path File in Archive
ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);
//Open File in Archive For Write
var OpenFileInArchive = FileInArchive.Open();
//Read Stream
// FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 4096 ;int TotalWrite=0;
while (TotalWrite != item.Bytes.Length)
{
if(TotalWrite+4096>item.Bytes.Length)
ReadByte=item.Bytes.Length-TotalWrite;
Array.Copy(item.Bytes, TotalWrite, ReadAllbytes, 0, ReadByte);
//Write Bytes
OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
TotalWrite += ReadByte;
}
OpenFileInArchive.Close();
}
archive.Dispose();
return memoryStream;
}
public void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName)
{
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
if (Directory.Exists(destinationDirectoryName)==false )
Directory.CreateDirectory(destinationDirectoryName);
//Loops through each file in the zip file
archive.ExtractToDirectory(destinationDirectoryName);
}
}
public void ExtractItems(string sourceArchiveFileName,List< string> _PathFilesinArchive, string destinationDirectoryName)
{
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
int PosResult = _PathFilesinArchive.IndexOf(file.FullName);
if (PosResult != -1)
{
//Create Folder
if (Directory.Exists( destinationDirectoryName + "\\" +Path.GetDirectoryName( _PathFilesinArchive[PosResult])) == false)
Directory.CreateDirectory(destinationDirectoryName + "\\" + Path.GetDirectoryName(_PathFilesinArchive[PosResult]));
Stream OpenFileGetBytes = file.Open();
FileStream FileStreamOutput = new FileStream(destinationDirectoryName + "\\" + _PathFilesinArchive[PosResult], FileMode.Create);
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0; int TotalRead = 0;
while (TotalRead != file.Length)
{
//Read Bytes
ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
TotalRead += ReadByte;
//Write Bytes
FileStreamOutput.Write(ReadAllbytes, 0, ReadByte);
}
FileStreamOutput.Close();
OpenFileGetBytes.Close();
_PathFilesinArchive.RemoveAt(PosResult);
}
if (_PathFilesinArchive.Count == 0)
break;
}
}
}
public ZipItem[] ExtractItems(string sourceArchiveFileName)
{
List< ZipItem> ZipItemsReading = new List<ZipItem>();
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
Stream OpenFileGetBytes = file.Open();
MemoryStream memstreams = new MemoryStream();
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0; int TotalRead = 0;
while (TotalRead != file.Length)
{
//Read Bytes
ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
TotalRead += ReadByte;
//Write Bytes
memstreams.Write(ReadAllbytes, 0, ReadByte);
}
memstreams.Position = 0;
OpenFileGetBytes.Close();
memstreams.Dispose();
ZipItemsReading.Add(new ZipItem(memstreams.ToArray(),file.FullName));
}
}
return ZipItemsReading.ToArray();
}
public ZipItem[] ExtractItems(string sourceArchiveFileName,List< string> _PathFilesinArchive)
{
List< ZipItem> ZipItemsReading = new List<ZipItem>();
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
int PosResult = _PathFilesinArchive.IndexOf(file.FullName);
if (PosResult!= -1)
{
Stream OpenFileGetBytes = file.Open();
MemoryStream memstreams = new MemoryStream();
byte[] ReadAllbytes = new byte[4096];//Capcity buffer
int ReadByte = 0; int TotalRead = 0;
while (TotalRead != file.Length)
{
//Read Bytes
ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
TotalRead += ReadByte;
//Write Bytes
memstreams.Write(ReadAllbytes, 0, ReadByte);
}
//Create item
ZipItemsReading.Add(new ZipItem(memstreams.ToArray(),file.FullName));
OpenFileGetBytes.Close();
memstreams.Dispose();
_PathFilesinArchive.RemoveAt(PosResult);
}
if (_PathFilesinArchive.Count == 0)
break;
}
}
return ZipItemsReading.ToArray();
}

Categories