昨天有童鞋在群裡面問:怎麼使用HttpClient發送文件?
之前我寫了一個《ABP小試牛刀之上傳文件》,主要體現的是服務端,上傳文件的動作是由前端小姐姐完成的, 我還真沒有用HttpClient編程方式發送過文件。
不過HttpClient的動作遵守Web協議,盲猜httpclient按照前端multipart/form-data媒體類型發送文件應該也是可行的。
花一個小時閱讀了MDN Web協議,寫就了HttpClient發送文件的實例, 看官自取。
頭腦風暴
我們跟隨常見的表單上傳文件思路來實現HttpClinet上傳文件。
multipart/form-data是一種由多部分表單域值組成的媒體類型,每部分由邊界線(一個由'--'開始的字符串)劃分。
如下面的表單, 有三個待提交input表單欄位
<form action="http://localhost:8000/" method="post" enctype="multipart/form-data">
<input type="text" name="myTextField">
<input type="checkbox" name="myCheckBox">Check</input>
<input type="file" name="myFile">
<button>Send the file</button>
</form>
選中文件,點擊[Send the file]按鈕,提交表單,會發出如下請求
請觀察由boundary劃分的每個表單域和值, 其中myFile是一個文件表單域, 這個文件域需要單獨指定Content-Type類型。
以上就是常規的Html表單上傳文件的協議分析,回到本文主題, 這次會使用HttpClient編碼形式發送只含有一個文件表單域的請求 (依舊利用的multipart/form-data媒體類型), 這也是下文的實現思路。
下面是httpclient向localhost:5000/upload地址上傳文件, 伺服器返回圖片的base64編碼字符串。
3.1 客戶端using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static readonly HttpClient client = new HttpClient();
static async Task Main()
{
try
{
byte[] bytes;
using (var bodyStream = new FileStream(@"D:\001.png", FileMode.Open))
{
using var m = new MemoryStream();
await bodyStream.CopyToAsync(m);
bytes = m.ToArray();
}
// 1. 準備文件表單域和值
var byteArrayContent = new ByteArrayContent(bytes);
byteArrayContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png");
// 2. 向MultipartFormDataContent插入準備好的文件表單域值, 注意MultipartFormDataContent是一個集合類型。
var response = await client.PostAsync("http://localhost:5000/upload", new MultipartFormDataContent(Guid.NewGuid().ToString())
{
{ byteArrayContent, "uploadedFile", "\"001ggg.png\""}
});
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
catch (HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", e.Message);
}
}
}
}
•請注意,我使用一個隨機的GUID做為每個表單域的劃分邊界,這裡我向MultipartFormDataContent只插入了一個文件表單閾值,這樣就做到了HttpClient發送文件。•文件表單域值: { byteArrayContent, "uploadedFile", "\"001ggg.png\""} 中的參數2: 欄位名稱很重要,要與下面服務端的參數名匹配。
3.2 服務端上傳文件的代碼在《ABP小試牛刀之上傳文件》一文已經體現,本次截取接收文件上傳的核心代碼
[Consumes("multipart/form-data")]
[Route("upload")]
[ProducesResponseType(typeof(Guid), 200)]
[HttpPost]
public async Task<string> UploadAsync(IFormFile uploadedFile)
{
var formFileName = uploadedFile.FileName;
if (!new[] { ".png", ".jpg", ".bmp" }.Any((item) => formFileName.EndsWith(item)))
{
throw new NotImplementedException("您上傳的文件格式必須為png、jpg、bmp中的一種");
}
byte[] bytes;
using (var bodyStream = uploadedFile.OpenReadStream())
{
using (var m = new MemoryStream())
{
await bodyStream.CopyToAsync(m);
bytes = m.ToArray();
}
}
var base64 = Convert.ToBase64String(bytes);
return base64;
}
碼甲哥從不打誑語,啟動客戶端/服務端
3.3 授人以漁成熟的技術必須有成熟的調試和監測手段!
成熟的技術必須有成熟的調試和監測手段!
成熟的技術必須有成熟的調試和監測手段!
每當做web開發出現阻塞的時候,我就掏出web利器Fiddler,跟著Fiddler去策馬奔騰吧。
# 全文總結1.對常規html表單上傳文件的功能,做協議級分析。2.根據分析結果,HttpClient使用同樣的姿勢發送文件: 使用multipart/form-data(多部分表單媒體類型)發起上傳請求。
[1] 媒體類型MIME: https://developer.mozilla.org/zhCN/docs/Web/HTTP/Basics_of_HTTP/MIME_types
我是有態度的馬甲,不追熱點,原創輸出 # 八股文 # 硬核乾貨 # 職場心得 #,歡迎關注。