C#中用HttpClient高效传输30MB+DOCX文档的最优性能方案
30MB+ DOCX文档API传输毫秒级优化方案
问题背景
我需要在两个API之间传输一个超过30MB的DOCX文档,目前试了两种方案都达不到毫秒级要求:
- JSON序列化传输:传输耗时约1秒
- Multipart分块传输:传输耗时约0.5秒,但合并分块还要额外花300毫秒
以下是我已经尝试过的代码:
已尝试方案:JSON序列化方式
客户端代码
var templatebyteArray = System.IO.File.ReadAllBytes(@"C:\Users\rprajapati\Downloads\TableData-out.docx"); // template byte array var authorization = HttpContext.Request.Headers["Authorization"]; var Test30MBDoc = new Test30MBDoc { CurrentTime = starttime, DocArray = templatebyteArray }; var content = Serialize(Test30MBDoc); using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, testUrl); request.Content = new StringContent(content, Encoding.UTF8, "application/json"); request.Headers.Add("Authorization", authorization[0]); var response = await client.SendAsync(request); }
服务端代码
[HttpPost] [Route("receiver1")] public async Task<IActionResult> Index(Test20MBDoc array) { try { // transport time take around 1 second } }
已尝试方案:Multipart分块方式
客户端代码
var templatebyteArray = System.IO.File.ReadAllBytes(@"C:\Users\rprajapati\Downloads\TableData-out.docx");; // template byte array int MAX_SUB_SIZE = 5242880; // 5*1024*1024 == 5MB MultipartFormDataContent multiPartContent = new MultipartFormDataContent(); int i = 1; foreach (byte[] chunk in templatebyteArray.Chunk(MAX_SUB_SIZE)) { ByteArrayContent byteArrayContent = new ByteArrayContent(chunk); byteArrayContent.Headers.Add("Content-Type", "application/octet-stream"); multiPartContent.Add(byteArrayContent, "byte content", $"chunk{i}"); i++; } var authorization = HttpContext.Request.Headers["Authorization"]; using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, testUrl); request.Content = multiPartContent; request.Headers.Add("Authorization", authorization[0]); var response = await client.SendAsync(request); }
服务端代码
[HttpPost] [Route("receiver")] public async Task<IActionResult> Index() { // transport time takes around 0.5 second but Need to convert chunks into template again it takes around 300 ms }
最优优化方案
1. 直接传输原始字节流(无额外封装)
这是开销最小的方式,跳过JSON序列化、Multipart分块的额外处理,直接发送文件字节流,服务端直接读取原始流,省去序列化和分块合并的时间。
客户端代码
var authorization = HttpContext.Request.Headers["Authorization"]; // 用Stream读取文件,避免一次性加载到内存 using (var client = new HttpClient()) using (var fileStream = new FileStream(@"C:\Users\rprajapati\Downloads\TableData-out.docx", FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 8192, useAsync: true)) { var request = new HttpRequestMessage(HttpMethod.Post, testUrl); request.Content = new StreamContent(fileStream); // 指定二进制流类型 request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); request.Headers.Add("Authorization", authorization[0]); var response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); }
服务端代码
[HttpPost] [Route("direct-receiver")] public async Task<IActionResult> ReceiveDirect() { // 直接读取请求体流,无需额外解析 using (var targetStream = new MemoryStream()) { await Request.Body.CopyToAsync(targetStream); targetStream.Position = 0; // 这里可以直接用targetStream处理DOCX,比如保存到文件或者直接传入处理逻辑 // 示例:await using var fileStream = new FileStream("received.docx", FileMode.Create); // await targetStream.CopyToAsync(fileStream); } return Ok(); }
2. 开启HTTP压缩(进一步降低传输时间)
DOCX本身是压缩格式,但开启Gzip或Brotli压缩能进一步减少传输体积,降低网络耗时。
客户端配置(启用自动解压)
var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Brotli }; using (var client = new HttpClient(handler)) { // 后续逻辑同直接字节流传输代码 }
服务端配置(ASP.NET Core)
在Program.cs中添加压缩中间件:
builder.Services.AddResponseCompression(options => { options.EnableForHttps = true; // 给二进制流添加压缩支持 options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream" }); // 优先用Brotli压缩(比Gzip效率更高) options.Providers.Add<BrotliCompressionProvider>(); options.Providers.Add<GzipCompressionProvider>(); }); // 注册压缩中间件(要放在路由中间件之前) app.UseResponseCompression();
3. 优化Multipart分块的合并逻辑(如果必须用分块)
如果业务上必须分块传输,优化服务端合并逻辑,避免不必要的内存拷贝,直接将分块流写入目标流:
优化后的服务端代码
[HttpPost] [Route("optimized-receiver")] public async Task<IActionResult> ReceiveOptimized() { var form = await Request.ReadFormAsync(); using (var targetStream = new MemoryStream()) { foreach (var file in form.Files) { // 直接将分块流复制到目标流,无需先转成byte数组 await file.CopyToAsync(targetStream); } targetStream.Position = 0; // 处理DOCX文档 } return Ok(); }
方案对比
| 方案 | 预估总耗时 | 优势 | 适用场景 |
|---|---|---|---|
| 直接字节流+压缩 | ~200-300ms | 开销最小,传输效率最高 | 无分块需求的场景 |
| 优化后Multipart分块 | ~400-500ms | 支持分块,合并耗时降低 | 需要分块校验/断点续传场景 |
| 原始JSON方式 | ~1000ms | 无需修改解析逻辑 | 小文件传输 |
内容的提问来源于stack exchange,提问作者user24973368
相关产品推荐
相关产品推荐

