使用 System.IO.Compression 在内存中创建 ZIP 存档
Posted
技术标签:
【中文标题】使用 System.IO.Compression 在内存中创建 ZIP 存档【英文标题】:Creating a ZIP archive in memory using System.IO.Compression 【发布时间】:2013-06-18 10:01:59 【问题描述】:我正在尝试使用MemoryStream
创建一个包含简单演示文本文件的 ZIP 存档,如下所示:
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);
如果我运行此代码,则会创建存档文件本身,但不会创建 foo.txt。
但是,如果我直接将MemoryStream
替换为文件流,则会正确创建存档:
using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
using (var archive = new ZipArchive(fileStream, FileMode.Create))
// ...
是否可以使用MemoryStream
来创建没有FileStream
的ZIP 存档?
【问题讨论】:
仅供参考:.ZipArchive 至少需要 .NET 4.5。参见 Rick Strahl 的 .NET 4.5 is an in-place replacement for .NET 4.0 ~~ 也参见 I didn't find “ZipFile” class in the “System.IO.Compression” namespace 如果您想要一个使用二进制数据而不是字符串的示例,这里有一个很好的解决方案:***.com/questions/48927574/… 【参考方案1】:我迟到了,但在某些情况下,您无法访问 ZipArchive
的构造函数来设置 leaveOpen
参数,并且您不希望将 ZIP 写入磁盘。就我而言,我在内部使用的 AsiceArchive
类创建了一个 ZipArchive
,但没有将 leaveOpen
设置为 true。
我创建了Stream
的一个子类,它将所有调用委托给一个内部流(使用 ReSharper 单击几下)。这个类不是一次性的,所以当ZipArchive
被释放时,内部流不会发生任何事情。
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;
像这样使用它:
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());
【讨论】:
【参考方案2】:感谢ZipArchive creates invalid ZIP file,我得到了:
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);
这表明我们需要在 ZipArchive
上调用 Dispose
才能使用它,正如 Amir 所暗示的那样,这很可能是因为它会将最终字节(如校验和)写入使其完整的存档。但是为了不关闭流,以便我们可以在您需要将true
作为第三个参数传递给ZipArchive
之后重新使用它。
【讨论】:
【参考方案3】:以防万一,如果有人想通过 SaveFileDialog 保存动态 zip 文件。
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();
【讨论】:
【参考方案4】:返回包含 zip 文件的流的函数
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;
如果需要,将 zip 写入文件流。
using (var fileStream = new FileStream(@"D:\Tools\DBExtractor\DBExtractor\bin\Debug\test.zip", FileMode.Create))
outStream.Position = 0;
outStream.WriteTo(fileStream);
`
【讨论】:
先将所有文件读入内存是极大的内存浪费。您最终会在内存中两次获得所有文件。一次在byteArray
和一次在ZipArchive
。更不用说您根本不需要将文件加载到内存中。使用流式传输,如所有其他现有答案所示。
@MartinPrikryl 问题是将 zip 文件写入内存的方法。这就是我使用内存的原因。当然最好的方法是写入本地
我知道问题是关于什么的。我的评论的重点是,您在内存中创建 ZIP 文件的实现非常低效。其他答案中的实现更好。【参考方案5】:
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);
【讨论】:
Controller.File
method has an overload that accepts Stream
。使用它来避免在内存中创建另一个 ZIP 文件副本。
天哪,这是谷歌搜索一个小时后的第一个直接答案。谢谢!
这个解决方案完美!谢谢!您甚至不需要移动字节数组 (byte[]
)。只需使用 MemoryStream 并且不要忘记在返回结果之前寻找零 (.Seek(0, SeekOrigin.Begin);
)
可以在 donet core 5.* w/WebApi 中确认这对我有用。【参考方案6】:
只是另一个版本的压缩而不写入任何文件。
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();
【讨论】:
【参考方案7】: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("========================");
【讨论】:
【参考方案8】:这是将实体转换为 XML 文件然后对其进行压缩的方法:
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();
【讨论】:
【参考方案9】: 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();
【讨论】:
【参考方案10】:将流的位置设置为 0,然后再将其复制到 zip 流。
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);
【讨论】:
感谢您提供直接的解决方案。赞!!【参考方案11】:你需要写完内存流然后读回缓冲区。
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 );
【讨论】:
以上是关于使用 System.IO.Compression 在内存中创建 ZIP 存档的主要内容,如果未能解决你的问题,请参考以下文章
使用 vb.net 和 System.IO.Compression (.Net 4.5) 压缩文件
我没有在“System.IO.Compression”命名空间中找到“ZipFile”类
System.IO.Compression.ZipFile.CreateFromDirectory - 在 Mac 上打开时没有文件夹结构
使用 System.IO.Compression 压缩 Windows 10 Documents 文件夹,不包括重解析点