.Net Framework中如何计算MultipartContent的Content-Length?
如何正确计算MultipartFormDataContent的总Content-Length?
我基于.NET Framework开发HTTP请求代码,用来发送包含StringContent、ByteArrayContent/StreamContent的multipart/form-data请求。目标接口要求必须携带Content-Length请求头,但MultipartContent的TryComputeLength是内部方法没法直接调用。我自己写的计算逻辑漏了StringContent的长度,导致Content-Length值错误。
我的原始代码:
MultipartFormDataContent multiPartContent = new MultipartFormDataContent(); if (contents.Attachments.Count > 0) { pof = LogMessage("Adding Attachment to Multipart-Content"); foreach (KeyValuePair<string, STGMedia> attachmentKVP in contents.Attachments) { STGMedia medium = attachmentKVP.Value; contentLength += medium.MediaStream.Length; multiPartContent.Add(GenerateAttachmentContent(medium, ActivityConfiguration.AttachmentFormat, medium.MediaType.MediaTypeMimeType), attachmentKVP.Key, medium.Name); } } foreach (KeyValuePair<string, string> kvp in contents.FormData) { multiPartContent.Add(new StringContent(kvp.Value), kvp.Key); } multiPartContent.Headers.ContentLength = contentLength;
要解决这个问题,得把multipart请求体的所有组成部分都算进去:每个子内容的实际长度、子内容对应的HTTP头部长度、每个子内容前的边界分隔符长度,还有结尾的收尾分隔符长度。下面是修正后的代码:
MultipartFormDataContent multiPartContent = new MultipartFormDataContent(); long contentLength = 0; // 获取multipart的边界字符串 string boundary = multiPartContent.Headers.ContentType.Parameters.First(p => p.Name == "boundary").Value; byte[] boundaryBytes = Encoding.ASCII.GetBytes($"--{boundary}\r\n"); byte[] endBoundaryBytes = Encoding.ASCII.GetBytes($"--{boundary}--\r\n"); // 处理附件内容 if (contents.Attachments.Count > 0) { pof = LogMessage("Adding Attachment to Multipart-Content"); foreach (KeyValuePair<string, STGMedia> attachmentKVP in contents.Attachments) { STGMedia medium = attachmentKVP.Value; var attachmentContent = GenerateAttachmentContent(medium, ActivityConfiguration.AttachmentFormat, medium.MediaType.MediaTypeMimeType); // 计算附件内容的长度 if (attachmentContent is StreamContent streamContent) { // 可Seek的流直接取Length,不可Seek的流要复制到MemoryStream获取长度 if (streamContent.ReadStream.CanSeek) { contentLength += streamContent.ReadStream.Length; } else { using (var ms = new MemoryStream()) { streamContent.ReadStream.CopyTo(ms); contentLength += ms.Length; } // 重置流位置,保证后续发送能正常读取 streamContent.ReadStream.Position = 0; } } else if (attachmentContent is ByteArrayContent byteContent) { contentLength += byteContent.Headers.ContentLength ?? 0; } // 计算该附件的HTTP头部字节数 var headers = attachmentContent.Headers.ToString(); contentLength += Encoding.ASCII.GetByteCount(headers + "\r\n"); // 添加当前子内容前的边界分隔符长度 contentLength += boundaryBytes.Length; multiPartContent.Add(attachmentContent, attachmentKVP.Key, medium.Name); } } // 处理FormData中的StringContent foreach (KeyValuePair<string, string> kvp in contents.FormData) { var stringContent = new StringContent(kvp.Value); // 获取StringContent实际使用的编码(默认是UTF-8) var encoding = stringContent.Headers.ContentType.CharSet != null ? Encoding.GetEncoding(stringContent.Headers.ContentType.CharSet) : Encoding.UTF8; // 计算字符串的字节长度 long contentByteLength = encoding.GetByteCount(kvp.Value); contentLength += contentByteLength; // 构建该FormData字段的HTTP头部(MultipartFormDataContent会自动添加这些头部) var dispositionHeader = $"Content-Disposition: form-data; name=\"{kvp.Key}\"\r\n"; var contentTypeHeader = stringContent.Headers.ContentType != null ? $"{stringContent.Headers.ContentType}\r\n" : ""; string headers = dispositionHeader + contentTypeHeader + "\r\n"; contentLength += Encoding.ASCII.GetByteCount(headers); // 添加当前子内容前的边界分隔符长度 contentLength += boundaryBytes.Length; multiPartContent.Add(stringContent, kvp.Key); } // 添加结尾的收尾分隔符长度 contentLength += endBoundaryBytes.Length; // 设置正确的Content-Length multiPartContent.Headers.ContentLength = contentLength;
关键注意点:
- 边界分隔符:每个子内容前都有
--{boundary}\r\n,结尾是--{boundary}--\r\n,这些字节数必须算进去 - 子内容头部:每个子内容对应的HTTP头部(比如Content-Disposition、Content-Type)也是请求体的一部分,要计算其ASCII编码的字节数
- StringContent编码:字符串的字节数要根据实际使用的编码计算,不能直接用字符串的字符长度
- StreamContent处理:如果流不可Seek,必须先复制到MemoryStream获取长度,之后要重置流的位置,否则发送请求时会读取失败
内容的提问来源于stack exchange,提问作者Doppelmeter
相关产品推荐
相关产品推荐

