使用 HttpContext OutputStream 写入 ZipArchive [英] Writing to ZipArchive using the HttpContext OutputStream
问题描述
我一直试图让 .NET 4.5 (System.IO.Compression.ZipArchive
) 中包含的新"ZipArchive 在 ASP.NET 站点中工作.但它似乎不喜欢写入 HttpContext.Response.OutputStream
的流.
I've been trying to get the "new" ZipArchive included in .NET 4.5 (System.IO.Compression.ZipArchive
) to work in a ASP.NET site. But it seems like it doesn't like writing to the stream of HttpContext.Response.OutputStream
.
我下面的代码示例会抛出
My following code example will throw
System.NotSupportedException: 不支持指定的方法
System.NotSupportedException: Specified method is not supported
一旦尝试写入流.
流上的 CanWrite
属性返回 true.
The CanWrite
property on the stream returns true.
如果我将 OutputStream 与指向本地目录的文件流交换,它就可以工作.什么给?
If I exchange the OutputStream with a filestream, pointing to a local directory, it works. What gives?
ZipArchive archive = new ZipArchive(HttpContext.Response.OutputStream, ZipArchiveMode.Create, false);
ZipArchiveEntry entry = archive.CreateEntry("filename");
using (StreamWriter writer = new StreamWriter(entry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
堆栈跟踪:
[NotSupportedException: Specified method is not supported.]
System.Web.HttpResponseStream.get_Position() +29
System.IO.Compression.ZipArchiveEntry.WriteLocalFileHeader(Boolean isEmptyFile) +389
System.IO.Compression.DirectToArchiveWriterStream.Write(Byte[] buffer, Int32 offset, Int32 count) +94
System.IO.Compression.WrappedStream.Write(Byte[] buffer, Int32 offset, Int32 count) +41
推荐答案
注意: 这已在 .Net Core 2.0 中修复.我不确定 .Net Framework 的修复状态是什么.
Note: This has been fixed in .Net Core 2.0. I'm not sure what is the status of the fix for .Net Framework.
Calbertoferreira 的回答提供了一些有用的信息,但结论大多是错误的.要创建存档,您不需要搜索,但您需要能够读取 Position
.
Calbertoferreira's answer has some useful information, but the conclusion is mostly wrong. To create an archive, you don't need seek, but you do need to be able to read the Position
.
根据文档,阅读Position
应该只支持可查找的流,但 ZipArchive
似乎甚至需要来自不可查找的流,即 一个错误.
According to the documentation, reading Position
should be supported only for seekable streams, but ZipArchive
seems to require this even from non-seekable streams, which is a bug.
因此,支持将 ZIP 文件直接写入 OutputStream
所需要做的就是将其包装在支持获取 Position
的自定义 Stream
中>.类似的东西:
So, all you need to do to support writing ZIP files directly to OutputStream
is to wrap it in a custom Stream
that supports getting Position
. Something like:
class PositionWrapperStream : Stream
{
private readonly Stream wrapped;
private long 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);
}
public override void Flush()
{
wrapped.Flush();
}
protected override void Dispose(bool disposing)
{
wrapped.Dispose();
base.Dispose(disposing);
}
// all the other required methods can throw NotSupportedException
}
使用这个,下面的代码将一个 ZIP 文件写入 OutputStream
:
Using this, the following code will write a ZIP archive into OutputStream
:
using (var outputStream = new PositionWrapperStream(Response.OutputStream))
using (var archive = new ZipArchive(outputStream, ZipArchiveMode.Create, false))
{
var entry = archive.CreateEntry("filename");
using (var writer = new StreamWriter(entry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
这篇关于使用 HttpContext OutputStream 写入 ZipArchive的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!