使用 HttpClient 上传多部分表单文件

Posted

技术标签:

【中文标题】使用 HttpClient 上传多部分表单文件【英文标题】:Uploading multipart form files with HttpClient 【发布时间】:2020-05-27 07:49:42 【问题描述】:

我正在尝试使用此处记录的 Support Bee API 创建附件: https://supportbee.com/api#create_attachment

我编写了一个服务,它使用HttpClient 创建并使用文件名发送请求。

如果我在 Postman 中测试,它会成功。我使用 form-data 作为正文,只是从 UI 中选择要上传的文件:

当我尝试通过我的HttpClient 服务上传它时它不起作用:

public async Task<string> CreateAttachmentAsync(string fileName)

    // "client" is HttpClient provided via D.I.

    MultipartFormDataContent content = new MultipartFormDataContent();
    content.Add(new StreamContent(new FileStream(fileName, FileMode.Open)), "files[]");

    using (HttpResponseMessage response = await client.PostAsync(
        "https://xxx.supportbee.com/attachments?auth_token=xxx",
        content))
    
        string responseString = await response.Content.ReadAsStringAsync();

        return responseString;
    

这会导致 500 内部服务器错误。检查MultipartFormDataContent 对象,我可以看到它的标头值正在自动设置:

内容类型:multipart/form-data;边界="c9be3778-4de5-4460-9929-adcaa6bdda79" 内容长度:164

我还尝试先将文件读取到字节数组并使用ByteArrayContent 而不是StreamContent 无济于事。响应没有提供任何有用的信息,但由于请求在 Postman 中有效,我的代码肯定有问题,但我不知道还能尝试什么。

编辑:我使用 Fiddler 进行了测试,以将成功的 Postman 请求与我的代码进行比较。这是 Postman 的请求:

发布 https://xxx.supportbee.com/attachments?auth_token=xxx HTTP/1.1 用户代理:PostmanRuntime/7.22.0 接受:/ 缓存控制: 无缓存邮递员令牌:f84d22fa-b4b1-4bf5-b183-916a786c6385 主机: xx.supportbee.com 内容类型:multipart/form-data; 边界=-------------714700821471353664787346 接受编码:gzip、deflate、br 内容长度:241 连接: 关闭

----------------714700821471353664787346 内容处置:表单数据;名称=“文件[]”;文件名="sample.txt" 内容类型:text/plain

这包含示例文本。 --------------714700821471353664787346--

以及来自我的代码的失败请求:

发布 https://xxx.supportbee.com/attachments?auth_token=xxx HTTP/1.1 主机:xxx.supportbee.com 接受:/ 接受编码: gzip、deflate、br 连接:关闭 内容类型:multipart/form-data; 边界="ea97cbc1-70ea-4cc4-9801-09f5feffc763" 内容长度:206

--ea97cbc1-70ea-4cc4-9801-09f5feffc763 内容处置:表单数据;名称=“文件[]”;文件名=样本;文件名*=utf-8''示例

这包含示例文本。 --ea97cbc1-70ea-4cc4-9801-09f5feffc763--

我可以看到的区别是 Postman 中的各个部分有自己的文件 Content-Type: text/plain 标头,而我的没有。我无法添加这个,因为如果我尝试 content.Headers.Add("Content-Type", "text/plain"); 它会失败并显示“无法添加值,因为标头 'Content-Type' 不支持多个值。”

【问题讨论】:

此代码using (var response = await client.SendAsync(message)) 发出GET 请求,您需要POST。首先,删除该行。您的HttpClient 可能在发送前检查BaseAddress。向我们展示responseString的价值 @RomanMarusyk 我在编辑问题时留下了一些不好的残留代码,我错过了,现在已被删除。它使用PostAsync 发送将发布的请求。 responseString 只是包含“我们很抱歉,但出了点问题 (500)”的 html 好的,尝试从 Postman 和您的应用中捕获请求并进行比较 (Fiddler)。你的代码看起来不错。 @RomanMarusyk 我实际上就是这样做的。添加到我的帖子中。 这能回答你的问题吗? C# HttpClient 4.5 multipart/form-data upload 【参考方案1】:

首先,请务必注意,500 响应类似于未处理的异常,即这是他们的错误,或多或少不可能确定您做错了什么。我建议向他们报告,虽然我不熟悉 Support Bee,但我希望他们有很好的支持人员可以帮助您解决问题。 :)

但是,如果您想玩猜谜游戏,我同意您成功的 Postman 调用与您的代码之间的细微差别是一个不错的起点。对于该标头,请注意 contentMultipartFormDataContent。您实际上想在 StreamContent 对象上设置它。

另外,查看 Postman 发送的请求标头,看看 Content-Disposition 是否包含 filename。如果 API 需要它,您可能还需要将它添加到您的代码中。

以下是如何做到这两点:

var fileContent = new StreamContent(File.OpenRead(path));
fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
content.Add(fileContent, "files[]", Path.GetFileName(path));

如果这不是问题,请查看 Postman 中请求正文的“原始”版本,以及这 11 个请求标头,看看是否能发现其他可能遗漏的内容。

【讨论】:

直接在StreamContent 对象上设置ContentType 标头使其工作。

以上是关于使用 HttpClient 上传多部分表单文件的主要内容,如果未能解决你的问题,请参考以下文章

异步 JQUERY 文件上传

C# 中的 HttpClient 多部分表单发布

Apache HttpClient 制作多部分表单帖子

如何在 Jetty HttpClient 中进行多部分/表单数据发布

无法在 iOS 中使用 NSURLSession 多部分表单数据上传文件

AngularJS:如何使用多部分表单实现简单的文件上传?