GZipStream обрезает последнюю часть XML - PullRequest
4 голосов
/ 06 сентября 2010

Я создал метод расширения под названием AddGZip, который выглядит следующим образом:

public static void AddGZip(this HttpResponse response)
{
    response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
    response.AppendHeader("Content-Encoding", "gzip");
}

Это очень урезанная версия кода:

var response = HttpContext.Current.Response;
var request = HttpContext.Current.Request;
var result = File.ReadAllText(path);
if (request.SupportsGZip)
{
  response.AddGZip();
}
response.Write(result);
response.Flush();

Когда вы просматриваете ответ в веб-браузере с поддержкой GZip, вы получаете сообщение об ошибке:

"Ошибка синтаксического анализа XML: незакрытый токен Расположение: http://webserver1/1234.xml Строка № 78, столбец 1: "

Когда я просматриваю источник, он в основном пропускал последний > из конца XML-файла. Итак, 1 или 2 байта.

Если я закомментирую строку AddGZip, она будет работать нормально. Однако я действительно хочу поддержать GZip, так как XML может быть довольно большим.

У кого-нибудь есть предложения для меня? Я пытался проверить множество блогов, но, похоже, нет решения для такого типа ошибки.

Dave

Ответы [ 2 ]

7 голосов
/ 06 сентября 2010

Существует проблема (или, возможно, действительно умная функция, которую я нигде не видел оправданной) с DeflateStream (GZipStream основывается на DeflateStream и наследует проблему *), когда сброс может привести к потере данных.

Response.Flush() промоет фильтр. Решение состоит в том, чтобы использовать оболочку, которая знает как о застежке-молнии, так и о нижележащей раковине, и только сбрасывает последнюю:

public enum CompressionType
{
    Deflate,
    GZip
}
/// <summary>
/// Provides GZip or Deflate compression, with further handling for the fact that
/// .NETs GZip and Deflate filters don't play nicely with chunked encoding (when
/// Response.Flush() is called or buffering is off.
/// </summary>
public class WebCompressionFilter : Stream
{
    private Stream _compSink;
    private Stream _finalSink;
    public WebCompressionFilter(Stream stm, CompressionType comp)
    {
        switch(comp)
        {
            case CompressionType.Deflate:
                _compSink = new DeflateStream((_finalSink = stm), CompressionMode.Compress);
                break;
            case CompressionType.GZip:
                _compSink = new GZipStream((_finalSink = stm), CompressionMode.Compress);
                break;
        }
    }
    public override bool CanRead
    {
        get
        {
            return false;
        }
    }
    public override bool CanSeek
    {
        get
        {
            return false;
        }
    }
    public override bool CanWrite
    {
        get
        {
            return true;
        }
    }
    public override long Length
    {
        get
        {
            throw new NotSupportedException();
        }
    }
    public override long Position
    {
        get
        {
            throw new NotSupportedException();
        }
        set
        {
            throw new NotSupportedException();
        }
    }
    public override void Flush()
    {
        //We do not flush the compression stream. At best this does nothing, at worse it
        //loses a few bytes. We do however flush the underlying stream to send bytes down the
        //wire.
        _finalSink.Flush();
    }
    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }
    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        _compSink.Write(buffer, offset, count);
    }
    public override void WriteByte(byte value)
    {
        _compSink.WriteByte(value);
    }
    public override void Close()
    {
        _compSink.Close();
        _finalSink.Close();
        base.Close();
    }
    protected override void Dispose(bool disposing)
    {
        if(disposing)
        {
            _compSink.Dispose();
            _finalSink.Dispose();
        }
        base.Dispose(disposing);
    }
}

Стоит также отметить, что большинство пользовательских агентов, поддерживающих gzip-кодирование, также поддерживают deflate-кодирование. Хотя улучшение размера с помощью deflate незначительно (буквально несколько байтов), некоторые библиотеки в некоторых архитектурах справляются с deflate значительно лучше (это касается как сжатия, так и распаковки), поэтому всегда стоит отдавать предпочтение deflate над gzip при сжатии HTTP.

0 голосов
/ 06 сентября 2010

Вы пытались добавить gzip через IIS?Есть вопрос об этом, так что посмотрите, что это такое.По сути, IIS выполняет сжатие, поэтому вам не нужно.

...