童鞋,[HttpClient发送文件的技术实践]请查收

简介: 1.对常规html表单上传文件的功能,做协议级分析。2.根据分析结果,HttpClient使用同样的姿势发送文件: 使用multipart/form-data(多部分表单媒体类型)发起上传请求。

01

荒腔走板


之前我写了一个《ABP小试牛刀之上传文件》,主要体现的是服务端,上传文件的动作是由前端小姐姐完成的, 我还真没有用HttpClient编程方式发送过文件。


不过HttpClient的动作遵守Web协议,盲猜httpclient按照前端multipart/form-data媒体类型发送文件应该也是可行的。


花一个小时阅读了MDN Web协议,写就了HttpClient发送文件的实例, 看官自取。


02

头脑风暴


我们跟随常见的表单上传文件思路来实现HttpClinet上传文件。


multipart/form-data是一种由多部分表单域值组成的媒体类型,每部分由边界线(一个由'--'开始的字符串)划分。


如下面的表单, 有三个待提交input表单字段


ea90c69649113b4a32a3d1c8f559798d.png


<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类型。


1dd4966116a93033b0ee9cc72b629278.png


03

照葫芦画瓢


以上就是常规的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;
}


码甲哥从不打诳语,启动客户端/服务端


c31f5b895b59bd6b534b360e11b95cbf.png


3.3 授人以渔


成熟的技术必须有成熟的调试和监测手段!


成熟的技术必须有成熟的调试和监测手段!


成熟的技术必须有成熟的调试和监测手段!


每当做web开发出现阻塞的时候,我就掏出web利器Fiddler,跟着Fiddler去策马奔腾吧。


1abf6f340af9cc348bc6ae8f31e4f4b5.png


# 全文总结


1.对常规html表单上传文件的功能,做协议级分析。


2.根据分析结果,HttpClient使用同样的姿势发送文件: 使用multipart/form-data(多部分表单媒体类型)发起上传请求。

相关文章
|
3月前
|
缓存 安全 JavaScript
技术分享:探索POST请求为何会发送两次的奥秘
【8月更文挑战第20天】在Web开发过程中,遇到POST请求被意外发送两次的情况并不罕见。这一现象不仅影响用户体验,还可能对服务器造成不必要的负担。本文将从多个维度深入剖析POST请求为何会发送两次,并分享相应的解决方案,助力开发者在日常工作中有效应对此类问题。
134 0
|
4月前
|
JSON 安全 应用服务中间件
【使用必读】服务端集成网易云信IM 即时通讯-回调说明篇(一)
【使用必读】服务端集成网易云信IM 即时通讯-回调说明篇(一)
77 0
|
4月前
|
开发工具 开发者
【使用必读】服务端集成网易云信IM 即时通讯-回调说明篇(二)
【使用必读】服务端集成网易云信IM 即时通讯-回调说明篇(二)
73 0
|
6月前
|
缓存 人工智能 API
【Python+微信】【企业微信开发入坑指北】2. 如何利用企业微信API主动给用户发应用消息
【Python+微信】【企业微信开发入坑指北】2. 如何利用企业微信API主动给用户发应用消息
237 0
|
小程序 Java 机器人
使用Java实现发送微信消息(附源码)_此程序在手再也不怕对象跟你闹了
此程序在手再也不怕女朋友跟你闹了!!!!自从有了女朋友比如:早安、晚安之类的问候语可不能断,但是也难免有时候会忘记那么该咋么办呢?很简单写一个程序么,近日闲来无趣想着用Java写一个自动发送微信的小程序,实现定时给指定的好友发送指定的消息,这不就很Nice了?本文主要包括实现的思路、代码的实现、打包为jar快捷方式!
155 0
|
小程序 前端开发 API
手把手教你接入抖音小程序发送模板消息通知
模板消息是指:按照一定的模板样式发送给用户的消息,顾名思义,它的内容必须限制在某一个模板框框内,只能做填空题,做不了主观题。
365 0
手把手教你接入抖音小程序发送模板消息通知
|
存储 JSON 小程序
unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)
unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)
418 0
unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)
|
API
autojs发微信通知
牙叔教程 简单易学 使用场景 给微信发送通知
622 0
GitHub 消息邮件通知太烦人?收下这份指南!
经常有朋友反馈说邮箱被 GitHub 的消息通知狂轰滥炸,各种无关的邮件提醒搞得很烦。
837 0
GitHub 消息邮件通知太烦人?收下这份指南!
|
XML 数据安全/隐私保护 开发者
微信开发被动回复消息:该公众号暂时无法提供服务,请稍后再试
最近小编在开发一个PC端站点以和公司公众号做对接,网站中有一个功能就是用户扫描站点上的二维码会在公众号上收到一条推送消息。具体实现流程如下: 微信会以事件的形式将用户操作相关数据以xml格式推送到站点,站点经过处理之后的数据也以xml的格式返回到微信服务器,微信服务器将解析后的数据推送到公众号上。
1262 1