【问题标题】:How do you store bytes to a var from a loop so that web response can be decompressed?如何将字节从循环存储到 var 以便可以解压缩 Web 响应?
【发布时间】:2020-04-28 19:18:53
【问题描述】:

我一直在使用 HTTPWebRequest/HTTPWebResponse 构建自己的 HTTP/1.0 代理服务器。部分原始代码将远程服务器的响应直接流式传输到客户端,如下所示:

if (response != null)
{
    List<Tuple<String, String>> responseHeaders = ProcessResponse(response);
    StreamWriter myResponseWriter = new StreamWriter(outStream);
    Stream responseStream = response.GetResponseStream();
    HttpStatusCode statusCode = response.StatusCode;
    string statusDesc = response.StatusDescription;

    Byte[] buffer;
    if (response.ContentLength > 0)
    {
        buffer = new Byte[response.ContentLength];
    }
    else
    {
        buffer = new Byte[BUFFER_SIZE];
    }

    int bytesRead;

    //send the response status and response headers
    WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
    WriteResponseHeaders(myResponseWriter, responseHeaders);

    while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        // this is the response body being streamed directly to the client browser
        outStream.Write(buffer, 0, bytesRead);
    }
}

我一直在尝试截取响应正文以便修改内容,但在此之前,如果远程服务器已对内容进行 gzip/br/deflate,我需要先解压缩该内容。这是我到目前为止所想出的,但是正如您从我的 cmets 中看到的那样,我只是无法弄清楚如何将字节流存储到一个 var 中,以便我可以将其发送以进行解压缩:

Byte[] buffer;
if (response.ContentLength > 0)
    buffer = new Byte[response.ContentLength];
else
    buffer = new Byte[BUFFER_SIZE];

int bytesRead;
var res = "";

// if the url and content type matches the criteria, then we want to edit it
if (hostPathMatch.Count > 0 && contentTypeMatch.Count > 0)
{
    while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
    {   
        // how to we send this response stream to a var so that the entire contents can be sent to decompress

        //res += UTF8Encoding.UTF8.GetString(buffer, 0, bytesRead); // this doesnt work as it mangles gzipped contents
    }

    //was the page compressed? check the content-encoding header.
    if (responseHeaders.Any(p => p.Item1.ToLower() == "content-encoding" && p.Item2.ToLower() == "gzip"))
    {
        Output._Log.Output_Log("CONTENT IS GZIPPED");
        res = Tools.Unzip(res); // expects byte[], returns UTF8
    }

    // THIS IS WHERE WE WILL MODIFY THE BODY CONTENTS
    res = res.Replace("Foo", "Bar Bar");

    // then we will re-compress

    // update the response headers with the correct content length after modification
    responseHeaders.RemoveAll(p => p.Item1 == "Content-Length");
    responseHeaders.Add(new Tuple<string, string>("Content-Length", res.Length));

    //send the response status and response headers
    WriteResponseStatus(statusCode, statusDesc, myResponseWriter); 
    WriteResponseHeaders(myResponseWriter, responseHeaders);
}
else // we didnt want to modify this file, so just stream it out directly to the browser
{
    //send the response status and response headers
    WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
    WriteResponseHeaders(myResponseWriter, responseHeaders);

    while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        outStream.Write(buffer, 0, bytesRead);
    }
}

【问题讨论】:

  • 如果您事先不知道完整的大小,您可以将所有收到的包保存在一个列表中......当完成将它们组合在一个数组中并随后解压缩。否则,只需创建一个具有完整响应大小的缓冲区并使用 'Array.CopyTo' 来填充缓冲区。
  • 你有机会提供一个例子吗?我明白你的意思,但我似乎无法弄清楚如何将流读入任何其他类型的 var

标签: c# arrays byte httpwebresponse gzipstream


【解决方案1】:

如果您事先不知道总大小,您可以执行以下操作

[...]

List<byte> data = new List<byte>();
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
    for(int i = 0; i < bytesRead; ++i)
        data.Add(buffer[i]);
}

var bytes = data.ToArray();

[...]

如果你提前知道,你可以使用类似的东西

[...]

int offset = 0;
byte[] data = new byte[TOTAL_SIZE];
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
    buffer.CopyTo(data, offset);
    offset += bytesRead;
}

[...]

请注意,上面的 sn-ps 没有经过任何测试,并且必须添加一些检查(例如超出范围等)。此外,根据用例,它可能不适合您的需求(例如,对于海量数据)。

PS:不要忘记处理/清理所有IDisposables。

【讨论】:

  • Robert,我尝试了 List 的第一种方法(因为我事先不知道总大小),但它似乎不起作用。虽然如果我尝试 UTF8Encoding.UTF8.GetString(bytes) 不会产生错误,但它只是空的。
  • 我已经对我的答案进行了更改(请参阅更改的data.Add(...)),您可以尝试一下。如果它仍然没有运行,我会尽快查看并测试代码 sn-p。
猜你喜欢
  • 2018-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-04
  • 1970-01-01
  • 2015-01-05
  • 2015-08-30
相关资源
最近更新 更多