private void _WriteEntryData(Stream s)
{
// Read in the data from the input stream (often a file in the filesystem),
// and write it to the output stream, calculating a CRC on it as we go.
// We will also compress and encrypt as necessary.
Stream input = null;
long fdp = -1L;
try
{
// Want to record the position in the zip file of the zip entry
// data (as opposed to the metadata). s.Position may fail on some
// write-only streams, eg stdout or System.Web.HttpResponseStream.
// We swallow that exception, because we don't care, in that case.
// But, don't set __FileDataPosition directly. It may be needed
// to READ the zip entry from the zip file, if this is a
// "re-stream" situation. In other words if the zip entry has
// changed compression level, or compression method, or (maybe?)
// encryption algorithm. In that case if the original entry is
// encrypted, we need __FileDataPosition to be the value for the
// input zip file. This s.Position is for the output zipfile. So
// we copy fdp to __FileDataPosition after this entry has been
// (maybe) restreamed.
fdp = s.Position;
}
catch (Exception) { }
try
{
// Use fileLength for progress updates, and to decide whether we can
// skip encryption and compression altogether (in case of length==zero)
long fileLength = SetInputAndFigureFileLength(ref input);
// Wrap a counting stream around the raw output stream:
// This is the last thing that happens before the bits go to the
// application-provided stream.
//
// Sometimes s is a CountingStream. Doesn't matter. Wrap it with a
// counter anyway. We need to count at both levels.
CountingStream entryCounter = new CountingStream(s);
Stream encryptor;
Stream compressor;
if (fileLength != 0L)
{
// Maybe wrap an encrypting stream around the counter: This will
// happen BEFORE output counting, and AFTER compression, if encryption
// is used.
encryptor = MaybeApplyEncryption(entryCounter);
// Maybe wrap a compressing Stream around that.
// This will happen BEFORE encryption (if any) as we write data out.
compressor = MaybeApplyCompression(encryptor, fileLength);
}
else
{
encryptor = compressor = entryCounter;
}
// Wrap a CrcCalculatorStream around that.
// This will happen BEFORE compression (if any) as we write data out.
var output = new Crisis.Ionic.Crc.CrcCalculatorStream(compressor, true);
// output.Write() causes this flow:
// calc-crc -> compress -> encrypt -> count -> actually write
if (this._Source == ZipEntrySource.WriteDelegate)
{
// allow the application to write the data
this._WriteDelegate(this.FileName, output);
}
else
{
// synchronously copy the input stream to the output stream-chain
byte[] buffer = new byte[BufferSize];
int n;
while ((n = SharedUtilities.ReadWithRetry(input, buffer, 0, buffer.Length, FileName)) != 0)
{
output.Write(buffer, 0, n);
OnWriteBlock(output.TotalBytesSlurped, fileLength);
if (_ioOperationCanceled)
break;
}
}
FinishOutputStream(s, entryCounter, encryptor, compressor, output);
}
finally
{
if (this._Source == ZipEntrySource.JitStream)
{
// allow the application to close the stream
if (this._CloseDelegate != null)
this._CloseDelegate(this.FileName, input);
}
else if ((input as FileStream) != null)
{
#if NETCF
input.Close();
#else
input.Dispose();
#endif
}
}
if (_ioOperationCanceled)
return;
// set FDP now, to allow for re-streaming
this.__FileDataPosition = fdp;
PostProcessOutput(s);
}