You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 04:25:22