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'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
.
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.
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
}
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("========================");
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…