站点图标 江湖人士

C#使用HttpClient发送文件3月工作记录

进入2023的3月了,又开始忙碌的码上生活了,前不久对接某个第3方的厂商要发送照片给它们,于是就用上了这个东东,C#使用HttpClient发送文件的场景较为常见,尤其是基于 .net 6以上来写一些底层的方法,这不,前几天就又封装了一下HttpClient发送文件到第3方的功能,或者你可以理解为其实就是C#的HttpClient上传文件。

为了使用 HttpClient 在请求中发送文件,将文件添加到 MultipartFormDataContent 对象中,并将该对象作为请求内容发送。这是一个例子:

var filePath = @"C:\jhrs.com.png";

using (var multipartFormContent = new MultipartFormDataContent())
{
	var fileStreamContent = new StreamContent(File.OpenRead(filePath));
	fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
	multipartFormContent.Add(fileStreamContent, name: "file", fileName: "jhrs.com.png");
	var response = await httpClient.PostAsync("https://jhrs.com/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}

这会发送以下 multipart/form-data POST 请求:

POST https://jhrs.com/files/ HTTP/1.1
Host: jhrs.com
Content-Type: multipart/form-data; boundary="44b2ed38-1ac7-4731-b2f4-f84bf159748d"
Content-Length: 7279

--44b2ed38-1ac7-4731-b2f4-f84bf159748d
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=jhrs.com.png; filename*=utf-8''jhrs.com.png

<bytes>

在本文中,我将解释有关 MultipartFormDataContent 的一些细节,并展示一些其他文件发送场景。

C#使用HttpClient发送文件3月工作记录

C#使用HttpClient发送文件

先前调用第3方的接口,要求发送的表单数据里面要求有文件,【你可以理解为混合表单内容,即包含文件和其它参数(值是字符串,就跟web表单一样)】因程序直接使用的是.net 7来开发的,那么理所当然的使用HttpClient发送文件喽。

这就要求你构造 MultipartFormDataContent 参数,然后调用它的Add方法,Add方法有多个重载版本,满足不同的使用场景。

public void Add(HttpContent content, string name, string fileName);

name参数是表单字段名称将其设置为 Web API 定义的参数名称(如果它使用自动映射)。

fileName参数是原始文件名当您处置 MultipartFormDataContent 时,它会处置您添加到其中的所有 HttpContent 对象。此外,当您处置 StreamContent 时,它会处置底层文件流。由于这种级联处理,您只需要一个using 块(如果您喜欢那种风格,也可以使用 using 声明)。简而言之,MultipartFormDataContent 处理 StreamContent 对象,后者处理 FileStream 对象。

发送一个文件多字段表单数据

当您需要发送文件时,您可能需要将它与某个实体相关联。换句话说,您需要将其它字段与文件一起发送。最简单的方法是将所有内容添加到 MultipartFormDataContent。

C#使用HttpClient发送文件3月工作记录

例如,假设您要发送文件并且需要包含Title和UserId。除了添加文件之外,您还可以将标题和UserID 字段添加到表单数据中,如下所示:

var filePath = @"C:\jhrs.com.png";

using (var multipartFormContent = new MultipartFormDataContent())
{
	multipartFormContent.Add(new StringContent("jhrs.com.user"), name: "UserId");
	multipartFormContent.Add(new StringContent("江湖人士网,一个分享赚被动收入的网站"), name: "Title");
	var fileStreamContent = new StreamContent(File.OpenRead(filePath));
	fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
	multipartFormContent.Add(fileStreamContent, name: "file", fileName: "jhrs.com.png");
	var response = await httpClient.PostAsync("https://jhrs.com/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}

这将发送以下 multipart/form-data 请求。请注意,它包括 Title 和 UserId 字段的部分:

POST https://jhrs.com/files/ HTTP/1.1
Host: jhrs.com
Content-Type: multipart/form-data; boundary="00d335a2-0389-48e1-85d9-0daf70c2879e"
Content-Length: 7519

--00d335a2-0389-48e1-85d9-0daf70c2879e
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=UserId

jhrs.com.user
--00d335a2-0389-48e1-85d9-0daf70c2879e
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=Title

江湖人士网,一个分享赚被动收入的网站
--00d335a2-0389-48e1-85d9-0daf70c2879e
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=jhrs.com.png; filename*=utf-8''jhrs.com.png

<bytes>

文件用字节数组发送

如果您已经有一个字节数组,并且不需要将文件作为文件流加载,那么您可以使用 ByteArrayContent 而不是 StreamContent。这是一个例子:

using (var multipartFormContent = new MultipartFormDataContent())
{
	var byteContent = new ByteArrayContent(fileBytesFromDatabase);
	byteContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
	multipartFormContent.Add(byteContent, name: "file", fileName: "jhrs.com.png");
	var response = await httpClient.PostAsync("https://jhrs.com/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}

这会生成以下请求:

POST https://jhrs.com/files/ HTTP/1.1
Host: jhrs.com
Content-Type: multipart/form-data; boundary="f4186b10-2cf4-4497-9a65-6e592d6cfce1"
Content-Length: 7243

--f4186b10-2cf4-4497-9a65-6e592d6cfce1
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=house.png; filename*=utf-8''jhrs.com.png

 <bytes>

发送多个文件

发送多个文件有两种方式:

您选择哪个选项取决于 Web API 的配置方式。这是第一个选项的示例——使用相同的名称参数发送多个文件:

var filePaths = new string[] { @"C:\jhrs.com.png", @"C:\znlive.com.png" };

using (var multipartFormContent = new MultipartFormDataContent())
{
	foreach(var filePath in filePaths)
	{
		var fileName = Path.GetFileName(filePath);
		var fileStreamContent = new StreamContent(File.OpenRead(filePath));
		fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
		multipartFormContent.Add(fileStreamContent, name: "files", fileName: fileName);
	}
	var response = await httpClient.PostAsync("https://jhrs.com/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}

这会发送以下请求:

POST https://jhrs.com/files/ HTTP/1.1
Host: jhrs.com
Content-Type: multipart/form-data; boundary="92f8b9da-896f-41ff-8709-85a0b8d0ef08"
Content-Length: 14442

--92f8b9da-896f-41ff-8709-85a0b8d0ef08
Content-Type: image/png
Content-Disposition: form-data; name=files; filename=house.png; filename*=utf-8''jhrs.com.png

<bytes>

--92f8b9da-896f-41ff-8709-85a0b8d0ef08
Content-Type: image/png
Content-Disposition: form-data; name=files; filename=car.png; filename*=utf-8''znlive.com.png

<bytes>

请注意,每个文件都放在自己的部分(由边界字符串分隔)。

设置文件的内容类型

图像文件“jhrs.com.png”的内容类型为“image/png”,它被添加为文件内容标题,包含以下行:

fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");

这会在多部分请求的文件部分中设置 Content-Type 标头:

--f4186b10-2cf4-4497-9a65-6e592d6cfce1
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=jhrs.com.png; filename*=utf-8''jhrs.com.png

如果您要集成的 Web API 要求您设置文件的 Content-Type,那么您必须明确设置它(它不会自动设置)。您可以根据文件的扩展名设置它(或者在适当的情况下对其进行硬编码)。这是一个例子:

var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
	[".png"] = "image/png",
	[".jpg"] = "image/jpeg",
	[".gif"] = "image/gif"
};
var filePath = @"C:\jhrs.com.png";
var extension = Path.GetExtension(filePath);
if (!map.TryGetValue(extension, out string contentType))
{
    throw new Exception("不允许发送的类型。");
}

var fileStreamContent = new StreamContent(File.OpenRead(filePath));
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

有关映射的完整列表,请参阅 .NET FileExtensionContentTypeProvider 源代码。由于该类只是 Dictionary<string, string> 的包装器,我建议添加您自己的映射,仅包含与您相关的文件扩展名(就像我在上面所做的那样)。

注意:如果你想使用 FileExtensionContentTypeProvider,它在 Microsoft.AspNetCore.StaticFiles 包中

退出移动版