C# 计算压缩级别为0的zip文件的大小
我正在为我们的web服务器编写功能,它应该从其他服务器下载几个文件,并将它们作为zip存档文件返回,而无需压缩 如果我知道所有下载文件的大小,如何确定ZIP存档的最终大小 这是我目前正在编写的代码。注释行导致ZIP存档损坏C# 计算压缩级别为0的zip文件的大小,c#,zip,C#,Zip,我正在为我们的web服务器编写功能,它应该从其他服务器下载几个文件,并将它们作为zip存档文件返回,而无需压缩 如果我知道所有下载文件的大小,如何确定ZIP存档的最终大小 这是我目前正在编写的代码。注释行导致ZIP存档损坏 public void Download() { var urls = Request.Headers["URLS"].Split(';'); Task<WebResponse>[] responseTasks = urls .Se
public void Download()
{
var urls = Request.Headers["URLS"].Split(';');
Task<WebResponse>[] responseTasks = urls
.Select(it =>
{
var request = WebRequest.Create(it);
return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse(null, null), request.EndGetResponse);
})
.ToArray();
Task.WaitAll(responseTasks);
var webResponses = responseTasks.Where(it => it.Exception == null).Select(it => it.Result);
var totalSize = webResponses.Sum(it => it.ContentLength + 32);
Response.ContentType = "application/zip";
Response.CacheControl = "Private";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
// Response.AddHeader("Content-Length", totalSize.ToString(CultureInfo.InvariantCulture));
var sortedResponses = webResponses.OrderBy(it => it.ContentLength);
var buffer = new byte[32 * 1024];
using (var zipOutput = new ZipOutputStream(Response.OutputStream))
{
zipOutput.SetLevel(0);
foreach (var response in sortedResponses)
{
var dataStream = response.GetResponseStream();
var ze = new ZipEntry(Guid.NewGuid().ToString() + ".jpg");
zipOutput.PutNextEntry(ze);
int read;
while ((read = dataStream.Read(buffer, 0, buffer.Length)) > 0)
{
zipOutput.Write(buffer, 0, read);
Response.Flush();
}
if (!Response.IsClientConnected)
{
break;
}
}
zipOutput.Finish();
}
Response.Flush();
Response.End();
}
publicsvoid下载()
{
var url=Request.Headers[“url”].Split(“;”);
任务[]响应任务=URL
.选择(it=>
{
var request=WebRequest.Create(it);
返回Task.Factory.fromsync(request.BeginGetResponse(null,null),request.EndGetResponse);
})
.ToArray();
Task.WaitAll(responseTasks);
var webResponses=responseTasks.Where(it=>it.Exception==null);
var totalSize=webResponses.Sum(it=>it.ContentLength+32);
Response.ContentType=“应用程序/zip”;
Response.CacheControl=“Private”;
Response.Cache.SetCacheability(HttpCacheability.NoCache);
//AddHeader(“Content Length”,totalSize.ToString(CultureInfo.InvariantCulture));
var sortedResponses=webResponses.OrderBy(it=>it.ContentLength);
var buffer=新字节[32*1024];
使用(var zipOutput=new ZipOutputStream(Response.OutputStream))
{
zipOutput.SetLevel(0);
foreach(分拣响应中的var响应)
{
var dataStream=response.GetResponseStream();
var ze=newzipentry(Guid.NewGuid().ToString()+“.jpg”);
zipOutput.PutNextEntry(ze);
int-read;
而((read=dataStream.read(buffer,0,buffer.Length))>0)
{
写入(缓冲区,0,读取);
Response.Flush();
}
如果(!Response.IsClientConnected)
{
打破
}
}
zipOutput.Finish();
}
Response.Flush();
Response.End();
}
ZIP文件由一些按文件记录和一些按存档记录组成。
它们具有复杂的结构,并且根据所使用的拱,其尺寸可能有所不同。
但是,如果将使用具有相同压缩选项的相同实现,则归档大小将仅取决于输入的大小和输入文件名的大小
因此,您可以使用1个和2个文件进行归档,并且知道它们的大小,再加上输入文件大小,再加上文件名大小,计算每个归档有效负载大小,每个文件有效负载大小,再加上归档大小与文件名的依赖关系(文件名在两个位置使用).我也遇到了同样的问题,阅读ZIP规范后,我想出了以下解决方案:
zip_size = num_of_files * (30 + 16 + 46) + 2 * total_length_of_filenames + total_size_of_files + 22
与:
- 30:本地文件头的固定部分
- 16:可选:数据描述符的大小
- 46:中央目录文件头的固定部分
- 22:中央目录记录(EOCD)结尾的
固定部分
这适用于我编写的ZIP实现。正如nickolay olshevsky指出的,其他压缩器
可能会做一些不同的事情。我也遇到了同样的问题,最终创建了一个假存档并跟踪大小
这样做的好处是,它可以与任何实现一起工作(比如来自System.IO.Compression的实现,它有许多分支,这取决于文件名编码或文件大小)
重要的部分是使用Stream.Null
而不是MemoryStream
,因此计算不使用内存
public long Size(FileItem[] files)
{
using (var ms = new PositionWrapperStream(Stream.Null))
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
var entry = archive.CreateEntry(file.Name, CompressionLevel.NoCompression);
using (var zipStream = entry.Open())
{
WriteZero(zipStream, file.Length);//the actual content does not matter
}
}
}
return ms.Position;
}
}
private void WriteZero(Stream target, long count)
{
byte[] buffer = new byte[1024];
while (count > 0)
{
target.Write(buffer, 0, (int) Math.Min(buffer.Length, count));
count -= buffer.Length;
}
}
PositionWrapperStream是一个简单的包装器,它只跟踪位置:
class PositionWrapperStream : Stream
{
private readonly Stream wrapped;
private int pos = 0;
public PositionWrapperStream(Stream wrapped)
{
this.wrapped = wrapped;
}
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return true; } }
public override long Position
{
get { return pos; }
set { throw new NotSupportedException(); }
}
public override void Write(byte[] buffer, int offset, int count)
{
pos += count;
wrapped.Write(buffer, offset, count);
}
//...other methods with throw new NotSupportedException();
}
最简单的方法可能是在内存流中创建zip并检查长度,然后再将其复制到响应流。ps:您的代码没有使用DotnetZip接口。它可能是SharpZipLib。非常感谢您,您刚刚为我节省了几个小时的规范阅读和实验!“这是InfoZip发布的Zip 3.0(2008年7月5日)。”在Amazon上,Linux非常接近您的公式。我不得不在第一笔钱的基础上再加上52。。。(30 + 16 + 46 + 52)