如何高效替换HttpContent中字符串并写入HttpResponse且不加载全量内容到内存
流式替换实现方案
要实现不加载全量内容到内存的替换,核心是通过滑动缓冲区处理跨读取块的字符串匹配,避免将整个响应内容加载到堆内存中,实现边读边替换边写入。
以下是可直接使用的实现代码:
using System.Buffers; using System.Text; // 先定义匹配规则和对应字节序列,注意编码要和HttpContent的内容编码一致,这里默认UTF8 const string oldValue = "example.com/thats-so-funny"; const string newValue = "example.com/thats-so-last-year"; byte[] oldBytes = Encoding.UTF8.GetBytes(oldValue); byte[] newBytes = Encoding.UTF8.GetBytes(newValue); // 缓冲区大小可根据实际场景调整,通常4KB/8KB即可 const int bufferSize = 4096; byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize + oldBytes.Length); try { await using var inputStream = await httpResponseMessage.Content.ReadAsStreamAsync(); await using var outputStream = context.Response.Body; int remainingBytesLength = 0; int bytesRead; // 循环读取输入流 while ((bytesRead = await inputStream.ReadAsync(buffer.AsMemory(remainingBytesLength, bufferSize))) > 0) { var totalSpan = buffer.AsSpan(0, remainingBytesLength + bytesRead); int processedPos = 0; int matchPos; // 扫描当前块查找匹配项 while ((matchPos = totalSpan.Slice(processedPos).IndexOf(oldBytes)) != -1) { // 写入匹配项之前的内容 if (matchPos > 0) { await outputStream.WriteAsync(totalSpan.Slice(processedPos, matchPos)); } // 写入替换后的内容 await outputStream.WriteAsync(newBytes); processedPos += matchPos + oldBytes.Length; } // 处理剩下的未匹配内容,保留最多oldBytes.Length-1个字节到下一次读取,避免跨块匹配被截断 remainingBytesLength = totalSpan.Length - processedPos; if (remainingBytesLength >= oldBytes.Length) { // 超过匹配长度的部分直接写入,不可能构成完整匹配 await outputStream.WriteAsync(totalSpan.Slice(processedPos, remainingBytesLength - (oldBytes.Length - 1))); remainingBytesLength = oldBytes.Length - 1; } // 将剩余未匹配的字节移到缓冲区开头,拼接到下一次读取的内容前面 totalSpan.Slice(processedPos, remainingBytesLength).CopyTo(buffer); } // 所有内容读取完成后,写入最后剩余的未匹配字节 if (remainingBytesLength > 0) { await outputStream.WriteAsync(buffer.AsSpan(0, remainingBytesLength)); } } finally { ArrayPool<byte>.Shared.Return(buffer); }
注意事项
- 要确保
oldBytes和newBytes使用的编码和原始HttpContent的内容编码完全一致,否则会出现匹配失败或者乱码问题 - 实现中使用了
ArrayPool共享缓冲区,避免频繁分配小对象带来的GC开销,性能表现更稳定 - 该方案的内存占用始终保持在缓冲区大小+匹配字符串长度的固定量级,不会随响应内容大小增长,适合处理大体积响应内容的替换场景
内容的提问来源于stack exchange,提问作者Draex_
相关产品推荐
相关产品推荐

