Не могу надуть с C# используя DeflateStream
Я использую моно, чтобы накачать / выкачать байты. Вот код:
public static byte[] Inflate(byte[] data)
{
using (MemoryStream inStream = new MemoryStream(data))
{
using (MemoryStream outStream = new MemoryStream())
{
using (DeflateStream decompressStream = new DeflateStream(inStream, CompressionMode.Decompress))
{
decompressStream.CopyTo(outStream);
}
return outStream.ToArray();
}
}
}
Входные данные: <789c3dca b9110020 0c04b196 bc9c3f7a 73f11030 281652d1 88b04195 1e742987 2f86258f acdec63d 6dcf0184 560cde> 47 байтов. Алгоритм ДЕФЛАТ.
Я успешно раздуваю те же данные на другой платформе, но через приведенный выше код выдает следующее исключение:
System.IO.IOException: Corrupted data ReadInternal
at System.IO.Compression.DeflateStreamNative.CheckResult (Int32 result, System.String where) [0x00000] in <filename unknown>:0
at System.IO.Compression.DeflateStreamNative.ReadZStream (IntPtr buffer, Int32 length) [0x00000] in <filename unknown>:0
at System.IO.Compression.DeflateStream.ReadInternal (System.Byte[] array, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
at System.IO.Compression.DeflateStream.Read (System.Byte[] dest, Int32 dest_offset, Int32 count) [0x00000] in <filename unknown>:0
at System.IO.Stream.CopyTo (System.IO.Stream destination, Int32 bufferSize) [0x00000] in <filename unknown>:0
at System.IO.Stream.CopyTo (System.IO.Stream destination) [0x00000] in <filename unknown>:0
1 ответ
Наконец, я использовал DotNetZip: http://dotnetzip.codeplex.com/ для решения проблемы.
public static byte[] Inflate(byte[] data)
{
int outputSize = 1024;
byte[] output = new Byte[ outputSize ];
bool expectRfc1950Header = true;
using ( MemoryStream ms = new MemoryStream())
{
ZlibCodec compressor = new ZlibCodec();
compressor.InitializeInflate(expectRfc1950Header);
compressor.InputBuffer = data;
compressor.AvailableBytesIn = data.Length;
compressor.NextIn = 0;
compressor.OutputBuffer = output;
foreach (var f in new FlushType[] { FlushType.None, FlushType.Finish } )
{
int bytesToWrite = 0;
do
{
compressor.AvailableBytesOut = outputSize;
compressor.NextOut = 0;
compressor.Inflate(f);
bytesToWrite = outputSize - compressor.AvailableBytesOut ;
if (bytesToWrite > 0)
ms.Write(output, 0, bytesToWrite);
}
while (( f == FlushType.None && (compressor.AvailableBytesIn != 0 || compressor.AvailableBytesOut == 0)) ||
( f == FlushType.Finish && bytesToWrite != 0));
}
compressor.EndInflate();
return ms.ToArray();
}
}
public static byte[] Deflate(byte[] data)
{
int outputSize = 1024;
byte[] output = new Byte[ outputSize ];
int lengthToCompress = data.Length;
// If you want a ZLIB stream, set this to true. If you want
// a bare DEFLATE stream, set this to false.
bool wantRfc1950Header = true;
using ( MemoryStream ms = new MemoryStream())
{
ZlibCodec compressor = new ZlibCodec();
compressor.InitializeDeflate(CompressionLevel.BestCompression, wantRfc1950Header);
compressor.InputBuffer = data;
compressor.AvailableBytesIn = lengthToCompress;
compressor.NextIn = 0;
compressor.OutputBuffer = output;
foreach (var f in new FlushType[] { FlushType.None, FlushType.Finish } )
{
int bytesToWrite = 0;
do
{
compressor.AvailableBytesOut = outputSize;
compressor.NextOut = 0;
compressor.Deflate(f);
bytesToWrite = outputSize - compressor.AvailableBytesOut ;
if (bytesToWrite > 0)
ms.Write(output, 0, bytesToWrite);
}
while (( f == FlushType.None && (compressor.AvailableBytesIn != 0 || compressor.AvailableBytesOut == 0)) ||
( f == FlushType.Finish && bytesToWrite != 0));
}
compressor.EndDeflate();
ms.Flush();
return ms.ToArray();
}
}
Если вы прочитаете мой комментарий, вы увидите, что я столкнулся с этой проблемой 18 часов назад, и хотя ответ на проблему находится здесь, в вашем ответе, он не очевиден. В вашем ответе есть набор переменныхwantRfc1950Header = true
и в вашем входном потоке первые два байта - это магические байты RFC 1950 78 9c
. ВSystem.IO.Compression.DeflateStream
ожидает необработанный поток RFC 1951, в котором пропущены эти два байта. Я полагаю, вы сможете использовать свой первоначальный пример, если вы отрежете эти первые два байта перед тем, как подать его в инфлятор.
С другой стороны, мне потребовалось более 18 часов, чтобы узнать, что мне нужно удалить два байта данных. С другой стороны, я гораздо лучше знаком с внутренним устройством zlib и кодирования Хаффмана.
Да, вы можете просто +2 байта, но я не уверен, что это пропускает поврежденные данные или работает во всех случаях?
// Note: Caller must Dispose/Close.
public DataReader ReadCompressedData()
{
// TODO: Optimize when using MemoryStream to use GetBuffer?
var uncompressedSize = ReadInt32();
var compressedSize = ReadInt32();
// Consuming 2 bytes for the 78 9C (Sometimes other like 78 DA)
// Unity / .Net Deflate Stream expects the data to not have this header.
// I could use the SharpZlib project to get around this or the DotNetZip.
// https://stackru.com/questions/762614/how-do-you-use-a-deflatestream-on-part-of-a-file
// http://www.faqs.org/rfcs/rfc1950.html
// https://stackru.com/questions/20850703/cant-inflate-with-c-sharp-using-deflatestream
//stream.Position += 2;
byte[] magic = ReadBytes(2);
compressedSize -= 2;
// I wonder if I should check these?
var compressedData = ReadBytes(compressedSize);
if (compressedData.Length != compressedSize)
{
Debug.LogError("Data read from underlying stream does not match specified size.");
}
// Decompress the data in the stream leaving it open.
// Note: Not sure how to stop DeflateStream gobbling up all data in the stream.
// using (var ds = new DeflateStream(BaseStream, CompressionMode.Decompress, true))
// {
//
// }
// Note: We are trusting that the decompressed data will fit completely into uncompressedSize.
var os = new MemoryStream(uncompressedSize);
using (var inputStream = new MemoryStream(compressedData))
{
using (var ds = new DeflateStream(inputStream, CompressionMode.Decompress))
{
ds.CopyTo(os);
}
}
// Reset the stream to the beginning for reading.
os.Position = 0;
return new DataReader(os);
}