我正在尝试使用以下代码:我收到了损坏的zip文件。为什么? 文件名似乎没问题。也许他们不是相对的名字,这就是问题所在?
private void trySharpZipLib(ArrayList filesToInclude)
{
// Response header
Response.Clear();
Response.ClearHeaders();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.StatusCode = 200; // http://community.icsharpcode.net/forums/p/6946/20138.aspx
long zipSize = calculateZipSize(filesToInclude);
string contentValue =
string.Format("attachment; filename=moshe.zip;"
); // + " size={0}", zipSize);
Response.ContentType = "application/octet-stream"; //"application/zip";
Response.AddHeader("Content-Disposition", contentValue);
Response.Flush();
using (ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream) )
{
zipOutputStream.SetLevel(0);
foreach (string f in filesToInclude)
{
string filename = Path.Combine(Server.MapPath("."), f);
using (FileStream fs = File.OpenRead(filename))
{
ZipEntry entry =
new ZipEntry(ZipEntry.CleanName(filename))
{
DateTime = File.GetCreationTime(filename),
CompressionMethod = CompressionMethod.Stored,
Size = fs.Length
};
zipOutputStream.PutNextEntry(entry);
byte[] buffer = new byte[fs.Length];
// write to zipoutStream via buffer.
// The zipoutStream is directly connected to Response.Output (in the constructor)
ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, zipOutputStream, buffer);
Response.Flush(); // for immediate response to user
} // .. using file stream
}// .. each file
}
Response.Flush();
Response.End();
}
答案 0 :(得分:14)
男孩,这是很多代码!使用DotNetZip,您的工作会更简单。假设一个HTTP 1.1客户端,这可以工作:
Response.Clear();
Response.BufferOutput = false;
string archiveName= String.Format("archive-{0}.zip", DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
Response.ContentType = "application/zip";
// see http://support.microsoft.com/kb/260519
Response.AddHeader("content-disposition", "attachment; filename=" + archiveName);
using (ZipFile zip = new ZipFile())
{
// filesToInclude is a IEnumerable<String> (String[] or List<String> etc)
zip.AddFiles(filesToInclude, "files");
zip.Save(Response.OutputStream);
}
// Response.End(); // will throw an exception internally.
// Response.Close(); // Results in 'Failed - Network error' in Chrome.
Response.Flush(); // See https://stackoverflow.com/a/736462/481207
// ...more code here...
如果要对zip进行密码加密,则在AddFiles()之前插入以下行:
zip.Password = tbPassword.Text; // optional
zip.Encryption = EncryptionAlgorithm.WinZipAes256; // optional
如果需要自解压存档,请将zip.Save()替换为zip.SaveSelfExtractor()。
附录; some people have commented to me DotNetZip“不好”,因为它在流式传输之前在内存中创建了整个ZIP。事实并非如此。当您调用 AddFiles 时,库会创建一个条目列表 - 表示要压缩的事物的状态的对象。在调用Save之前,没有进行压缩或加密。如果指定Save()调用的流,则所有压缩的字节将直接流式传输到客户端。
在SharpZipLib模型中,可以创建一个条目,然后将其流出,然后创建另一个条目,然后将其流出,依此类推。使用DotNetZip,您的应用程序首先创建完整的条目列表,然后将它们全部流出。这两种方法都不一定比其他方法“更快”,但对于长文件列表,例如30,000,使用SharpZipLib时,第一字节的时间会更快。另一方面,我不建议动态创建包含30,000个条目的zip文件。
编辑
从DotNetZip v1.9开始,DotNetZip也支持ZipOutputStream。我仍然认为按照我在这里展示的方式做事更简单。
有些人的情况是,所有用户都拥有“大部分相同”的zip内容,但每个用户的文件都有不同。 DotNetZip也很擅长这一点。您可以从文件系统文件中读取zip存档,更新一些条目(添加一些,删除一些等),然后保存到Response.OutputStream。在这种情况下,DotNetZip不会重新压缩或重新加密您未更改的任何条目。快多了。
当然,DotNetZip适用于任何.NET应用程序,而不仅仅是ASP.NET。所以你可以保存到任何流。
如果您想了解更多信息,请查看site或在dotnetzip forums上发帖。
答案 1 :(得分:2)
不太确定如何在ASP.NET中执行此操作(以前没有尝试过),但通常如果HTTP客户端支持HTTP v1.1(由其请求的版本指示),服务器可以发送一个'Transfer-Encoding'响应头,指定'chunked',然后在多个数据块可用时发送响应数据。这允许在您不知道最终数据大小的情况下实时传输数据(因此无法设置'Content-Length'响应头)。有关更多详细信息,请查看RFC 2616第3.6节。
答案 2 :(得分:1)
对于那些会错过SharpZipLib的ZipOutputStream的人来说,这里有一个简单的代码,可以使用DotNetZip“常规的.NET流方式”。
然而,请注意,与SharpZipLib之类的真实即时流媒体解决方案相比,它效率低,因为它在实际调用DotNetZip.Save()函数之前使用内部MemoryStream。但不幸的是,SharpZibLib还没有体育EAS加密(当然也从未)。我们希望Cheeso能在短期内在dotNetZip中添加此功能吗? ; - )
/// <summary>
/// DotNetZip does not support streaming out-of-the-box up to version v1.8.
/// This wrapper class helps doing so but unfortunately it has to use
/// a temporary memory buffer internally which is quite inefficient
/// (for instance, compared with the ZipOutputStream of SharpZibLib which has other drawbacks besides).
/// </summary>
public class DotNetZipOutputStream : Stream
{
public ZipFile ZipFile { get; private set; }
private MemoryStream memStream = new MemoryStream();
private String nextEntry = null;
private Stream outputStream = null;
private bool closed = false;
public DotNetZipOutputStream(Stream baseOutputStream)
{
ZipFile = new ZipFile();
outputStream = baseOutputStream;
}
public void PutNextEntry(String fileName)
{
memStream = new MemoryStream();
nextEntry = fileName;
}
public override bool CanRead { get { return false; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return true; } }
public override long Length { get { return memStream.Length; } }
public override long Position
{
get { return memStream.Position; }
set { memStream.Position = value; }
}
public override void Close()
{
if (closed) return;
memStream.Position = 0;
ZipFile.AddEntry(nextEntry, Path.GetDirectoryName(nextEntry), memStream);
ZipFile.Save(outputStream);
memStream.Close();
closed = true;
}
public override void Flush()
{
memStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("Read");
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("Seek");
}
public override void SetLength(long value)
{
memStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
memStream.Write(buffer, offset, count);
}
}
答案 3 :(得分:0)
尝试添加以下标题。
Response.AddHeader("Content-Length", zipSize);
我知道这之前给我带来了问题。
修改强>
这些其他2也可能有所帮助:
Response.AddHeader("Content-Description", "File Transfer");
Response.AddHeader("Content-Transfer-Encoding", "binary");
答案 4 :(得分:0)
您是否尝试在刷新响应之前刷新ZipOutputStream? 你可以在客户端保存zip并在zip工具中测试它吗?
答案 5 :(得分:0)
Necromancing。
根据Cheeso在评论中推荐的DotNetZip v1.9 +开始,使用闭包,这是如何正确完成的:
public static void Run()
{
using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
{
for (int i = 1; i < 11; ++i)
{
zip.AddEntry("LeaseContractForm_" + i.ToString() + ".xlsx", delegate(string filename, System.IO.Stream output)
{
// ByteArray from ExecuteReport - only ONE ByteArray at a time, because i might be > 100, and ba.size might be > 20 MB
byte[] ba = Portal_Reports.LeaseContractFormPostProcessing.ProcessWorkbook();
output.Write(ba, 0, ba.Length);
});
} // Next i
using (System.IO.Stream someStream = new System.IO.FileStream(@"D:\test.zip", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
{
zip.Save(someStream);
}
} // End Using zip
} // End Sub Run
VB.NET变体,以防任何人需要它(请注意,这只是一个测试;实际上,它将使用不同的in_contract_uid和in_premise_uid为循环中的每个步骤调用):
Imports System.Web
Imports System.Web.Services
Public Class test
Implements System.Web.IHttpHandler
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim in_contract_uid As String = context.Request.Params("in_contract_uid")
Dim in_premise_uid As String = context.Request.Params("in_premise_uid")
If String.IsNullOrWhiteSpace(in_contract_uid) Then
in_contract_uid = "D57A62D7-0FEB-4FAF-BB09-84106E3E15E9"
End If
If String.IsNullOrWhiteSpace(in_premise_uid) Then
in_premise_uid = "165ECACA-04E6-4DF4-B7A9-5906F16653E0"
End If
Dim in_multiple As String = context.Request.Params("in_multiple")
Dim bMultiple As Boolean = False
Boolean.TryParse(in_multiple, bMultiple)
If bMultiple Then
Using zipFile As New Ionic.Zip.ZipFile
For i As Integer = 1 To 10 Step 1
' Dim ba As Byte() = Portal_Reports.LeaseContractFormReport.GetLeaseContract(in_contract_uid, in_premise_uid) '
' zipFile.AddEntry("LeaseContractForm_" + i.ToString() + ".xlsx", ba) '
zipFile.AddEntry("LeaseContractForm_" + i.ToString() + ".xlsx", Sub(filename As String, output As System.IO.Stream)
Dim ba As Byte() = Portal_Reports.LeaseContractFormReport _
.GetLeaseContract(in_contract_uid, in_premise_uid)
output.Write(ba, 0, ba.Length)
End Sub)
Next i
context.Response.ClearContent()
context.Response.ClearHeaders()
context.Response.ContentType = "application/zip"
context.Response.AppendHeader("content-disposition", "attachment; filename=LeaseContractForm.zip")
zipFile.Save(context.Response.OutputStream)
context.Response.Flush()
End Using ' zipFile '
Else
Dim ba As Byte() = Portal_Reports.LeaseContractFormReport.GetLeaseContract(in_contract_uid, in_premise_uid)
Portal.ASP.NET.DownloadFile("LeaseContractForm.xlsx", "attachment", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ba)
End If
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class