童鞋,[HttpClient發(fā)送文件的技術(shù)實(shí)踐]請(qǐng)查收

? ? 昨天有童鞋在群里面問(wèn):怎么使用HttpClient發(fā)送文件?
01
荒腔走板
? ? ? 之前我寫(xiě)了一個(gè)《ABP小試牛刀之上傳文件》,主要體現(xiàn)的是服務(wù)端,上傳文件的動(dòng)作是由前端小姐姐完成的, 我還真沒(méi)有用HttpClient編程方式發(fā)送過(guò)文件。
不過(guò)HttpClient的動(dòng)作遵守Web協(xié)議,盲猜httpclient按照前端multipart/form-data媒體類型發(fā)送文件應(yīng)該也是可行的。
花一個(gè)小時(shí)閱讀了MDN Web協(xié)議,寫(xiě)就了HttpClient發(fā)送文件的實(shí)例, 看官自取。
02
頭腦風(fēng)暴
? ? ? 我們跟隨常見(jiàn)的表單上傳文件思路來(lái)實(shí)現(xiàn)HttpClinet上傳文件。
multipart/form-data是一種由多部分表單域值組成的媒體類型,每部分由邊界線(一個(gè)由'--'開(kāi)始的字符串)劃分。
如下面的表單, 有三個(gè)待提交input表單字段

選中文件,點(diǎn)擊[Send the file]按鈕,提交表單,會(huì)發(fā)出如下請(qǐng)求
請(qǐng)觀察由
boundary劃分的每個(gè)表單域和值, 其中myFile是一個(gè)文件表單域,?這個(gè)文件域需要單獨(dú)指定Content-Type類型。

03
照葫蘆畫(huà)瓢
? ? ? ?以上就是常規(guī)的Html表單上傳文件的協(xié)議分析,回到本文主題, 這次會(huì)使用HttpClient編碼形式發(fā)送只含有一個(gè)文件表單域的請(qǐng)求?(依舊利用的multipart/form-data媒體類型), 這也是下文的實(shí)現(xiàn)思路。
下面是httpclient向localhost:5000/upload地址上傳文件, 服務(wù)器返回圖片的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.?準(zhǔn)備文件表單域和值
????????????????var?byteArrayContent?=?new?ByteArrayContent(bytes);
????????????????byteArrayContent.Headers.ContentType?=?MediaTypeHeaderValue.Parse("image/png");????????????????// 2. ?向MultipartFormDataContent插入準(zhǔn)備好的文件表單域值,?注意MultipartFormDataContent是一個(gè)集合類型。
????????????????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);
????????????}
????????}
????}
}
?請(qǐng)注意,我使用一個(gè)隨機(jī)的GUID做為每個(gè)表單域的劃分邊界,這里我向MultipartFormDataContent只插入了一個(gè)文件表單閾值,這樣就做到了HttpClient發(fā)送文件。?文件表單域值:? { byteArrayContent, "uploadedFile", "\"001ggg.png\""}?中的參數(shù)2:?字段名稱很重要,要與下面服務(wù)端的參數(shù)名匹配。
3.2 服務(wù)端
上傳文件的代碼在《ABP小試牛刀之上傳文件》一文已經(jīng)體現(xiàn),本次截取接收文件上傳的核心代碼
[Consumes("multipart/form-data")]
[Route("upload")]
[ProducesResponseType(typeof(Guid),?200)]
[HttpPost]
public?async?Task?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;
}
碼甲哥從不打誑語(yǔ),啟動(dòng)客戶端/服務(wù)端

3.3 授人以漁
成熟的技術(shù)必須有成熟的調(diào)試和監(jiān)測(cè)手段!
成熟的技術(shù)必須有成熟的調(diào)試和監(jiān)測(cè)手段!
成熟的技術(shù)必須有成熟的調(diào)試和監(jiān)測(cè)手段!

每當(dāng)做web開(kāi)發(fā)出現(xiàn)阻塞的時(shí)候,我就掏出web利器Fiddler,跟著Fiddler去策馬奔騰吧。

#?全文總結(jié)
1.對(duì)常規(guī)html表單上傳文件的功能,做協(xié)議級(jí)分析。2.根據(jù)分析結(jié)果,HttpClient使用同樣的姿勢(shì)發(fā)送文件: 使用multipart/form-data(多部分表單媒體類型)發(fā)起上傳請(qǐng)求。
[1]?媒體類型MIME:?https://developer.mozilla.org/zhCN/docs/Web/HTTP/Basics_of_HTTP/MIME_types
我是有態(tài)度的馬甲,不追熱點(diǎn),原創(chuàng)輸出 # 八股文 # 硬核干貨 # 職場(chǎng)心得 #,歡迎關(guān)注。
點(diǎn)分享
點(diǎn)點(diǎn)贊
點(diǎn)在看
