【问题标题】:DeflateStream: compress files while readingDeflateStream:在阅读时压缩文件
【发布时间】:2012-05-18 04:35:35
【问题描述】:

我正在尝试通过WCF(一些 GB)发送大量数据。我想在使用 Streams 读取文件时压缩文件,但看起来 DeflateStream 有两种模式:

  • 压缩(写入流)
  • 解压(读取流)

这些模式都不适用于我的情况。我想从磁盘读取一个未压缩的文件并通过 WCF 返回一个压缩流。

有没有办法这样做,或者我必须使用临时文件(或 MemoryStream)?

是缺少功能还是由于某种原因无法实现?

【问题讨论】:

  • 您确定从new DeflateStream(fileStream, CompressionMode.Compress) 阅读不起作用吗?
  • new DeflateStream(fi.OpenRead(), CompressionMode.Compress) 抛出“基本流不可写”
  • 此外,DeflateStream.Read 帮助说:“将许多解压缩的字节读入指定的字节数组。” DeflateStream.Write 说:“将压缩字节从指定的字节数组写入底层流”
  • 在纸上画图看看你想压缩什么。 (文件 -> 读取文件的方法 -> WCF 通道 -> 可选网络 -> 接收器)。您很有可能在错误的时间尝试压缩(即发送压缩字节数组或正确进行响应流)。
  • 很简单,我想从磁盘中读取一个Stream,边读取边压缩,然后通过WCF发送可读流。问题是它只有在写 :S 时才会压缩

标签: c# wcf stream compression deflate


【解决方案1】:

你应该有类似的东西:

public void CompressData(Stream uncompressedSourceStream, Stream compressedDestinationStream)
{
    using (DeflateStream compressionStream = new DeflateStream(compressedDestinationStream, CompressionMode.Compress))
    {
        uncompressedSourceStream.CopyTo(compressionStream);
    }
}

public void DecompressData(Stream compressedSourceStream, Stream uncompressedDestinationStream)
{
    using (DeflateStream decompressionStream = new DeflateStream(uncompressedDestinationStream, CompressionMode.Decompress))
    {
        compressedSourceStream.CopyTo(decompressionStream);
    }
}

using (FileStream sourceStream = File.OpenRead(@"C:\MyDir\MyFile.txt))
using (FileStream destinationStream = File.OpenWrite(@"C:\MyDir\MyCompressedFile.txt.cp"))
{
    CompressData(sourceStream, destinationStream)
}

另外,请注意,您可能必须更改应用程序的 .config 文件中的 WCF 设置以允许传输非常大的内容。

【讨论】:

  • 如果这样做,当您调用compressed.Read(...) 时,它会抛出“Stream 不支持读取。”。你只是延迟了之前在构造函数中的异常。
  • 已编辑。我意识到有关 DeflateStream 的文档尚不清楚。 DeflateStream 的构造函数流实际上是您的目标流。
  • 但是目标流是可写流。我需要一个可读流才能将其发送到 WCF。 codeproject.com/Articles/20364/…。我知道我可以存储一个文件并在之后删除它,但看起来很笨重。
  • 也许这样会更清楚(再次编辑)。只要您已经有一个可写的流,目标流就可以是您想要的任何东西。
  • 好的...所以在查看了您提供的项目链接后,我发现您的问题是,在一个方向上,您需要能够向RemoteFileInfo 类。一种解决方案是基于队列构建自定义流,以避免一次将全部内容存储在内存中。最简单的方法可能是只编写压缩文件,然后在完成后将其删除。
【解决方案2】:

您在读取文件时似乎正在尝试压缩。 deflatestream 的写入方式,压缩必须作为写入的一部分进行。尝试包装您通过线路发送的流,而不是您从磁盘读取的流。如果它们相同,则需要一个中间流。

【讨论】:

  • 我是这么想的。当调用者尝试读取时,我需要某种类型的 Stream Reverser 写入缓冲区。还有一个读写或类似的东西。太可怕了……
  • 是的,这显然是行不通的。也许这会有所帮助? msdn.microsoft.com/en-us/library/ms751458.aspx
  • 虽然不完全一样。我最终在 IIS 级别启用了 WCF 压缩,如下所示:hanselman.com/blog/… 谢谢大家
【解决方案3】:

尝试使用这些方法来压缩和解压缩字节数组。

    private static byte[] Compress(byte[] data)
    {
        byte[] retVal;
        using (MemoryStream compressedMemoryStream = new MemoryStream())
        {
            DeflateStream compressStream = new DeflateStream(compressedMemoryStream, CompressionMode.Compress, true);
            compressStream.Write(data, 0, data.Length);
            compressStream.Close();
            retVal = new byte[compressedMemoryStream.Length];
            compressedMemoryStream.Position = 0L;
            compressedMemoryStream.Read(retVal, 0, retVal.Length);
            compressedMemoryStream.Close();
            compressStream.Close();
        }
        return retVal;
    }



    private static byte[] Decompress(byte[] data)
    {
        byte[] retVal;
        using (MemoryStream compressedMemoryStream = new MemoryStream())
        {
            compressedMemoryStream.Write(data, 0, data.Length);
            compressedMemoryStream.Position = 0L;
            MemoryStream decompressedMemoryStream = new MemoryStream();
            DeflateStream decompressStream = new DeflateStream(compressedMemoryStream, CompressionMode.Decompress);
            decompressStream.CopyTo(decompressedMemoryStream);
            retVal = new byte[decompressedMemoryStream.Length];
            decompressedMemoryStream.Position = 0L;
            decompressedMemoryStream.Read(retVal, 0, retVal.Length);
            compressedMemoryStream.Close();
            decompressedMemoryStream.Close();
            decompressStream.Close();
        }
        return retVal;
}

【讨论】:

  • 这意味着在内存中保存 Gb 的数据。我更喜欢将压缩后的文件保存在磁盘上。
【解决方案4】:

我试图创建一个 Stream,每当调用 Read 时:

  • 从源文件中读取一大块数据
  • 将块写入连接到 MemoryStream 的 DeflateStream
  • MemoryStream 的内容复制到读取buffer 参数。

当然,这会更困难,因为两个流的大小并不相似。

最后我取消了这个选项,因为我没有办法在不完全压缩的情况下预测生成的压缩文件的大小。

但是,读取文件能够预测文件大小,因此也许可以使用DeflateStream 的另一个实现。

希望它能帮助其他迷失的灵魂......

【讨论】:

    【解决方案5】:

    您可以将 DeflateStream 包装在您自己的流中。每次您想从压缩流中读取数据时,都必须将字节输入 deflatestream,直到它写入缓冲区。然后,您可以从该缓冲区返回字节。

    public class CompressingStream : Stream
    {
        private readonly DeflateStream _deflateStream;
        private readonly MemoryStream _buffer;
        private Stream _inputStream;
        private readonly byte[] _fileBuffer = new byte[64 * 1024];
    
        public CompressingStream(Stream inputStream)
        {
            _inputStream = inputStream;
            _buffer = new MemoryStream();
            _deflateStream = new DeflateStream(_buffer, CompressionMode.Compress, true);
        }
    
        public override int Read(byte[] buffer, int offset, int count)
        {
            while (true)
            {
                var read = _buffer.Read(buffer, offset, count);
    
                if (read > 0) return read;
    
                if (_inputStream == null) return 0;
    
                _buffer.Position = 0;
                read = _inputStream.Read(_fileBuffer, 0, _fileBuffer.Length);
                if (read == 0)
                {
                    _inputStream.Close();
                    _inputStream = null;
                    _deflateStream.Close();
                }
                else
                {
                    _deflateStream.Write(_fileBuffer, 0, read);
                }
                _buffer.SetLength(_buffer.Position);
                _buffer.Position = 0;
            }
        }
    
        public override bool CanRead
        {
            get { return true; }
        }
    #region Remaining overrides...
    }
    

    每当 wcf 从流中读取时,压缩流将写入压缩 DeflateStream,直到它从输出缓冲区 (_buffer) 中读取。 这很丑陋,但它有效。

    【讨论】:

      【解决方案6】:

      适用于 blob 的 Azure API 可以替代 UploadStream(stream)。您可以使用 OpenWrite() 获取流。所以现在您可以控制推送字节,因此可以在将内容流式传输到服务时进行压缩

      using (var uploadStream = blob.OpenWrite())
      using (var deflateStream = new DeflateStream(uploadStream, CompressionMode.Compress))
      {
          stream.CopyTo(deflateStream);
      }
      

      我没有检查 WCF API,但如果你不能这样做,我会感到惊讶。

      【讨论】:

        猜你喜欢
        • 2016-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-16
        相关资源
        最近更新 更多