zoukankan      html  css  js  c++  java
  • 童鞋,[HttpClient发送文件] 的技术实践请查收

    1.荒腔走板

    前几天有个童鞋在群里面问:怎么使用HttpClient发送文件?

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

    不过想来,Web协议都是一样的,类比前端发送文件,httpclient按照multipart/form-data媒体类型应该也是可以发送的。

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

    2.头脑风暴

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

    multipart/form-data是一种多部分的文档格式,每部分由边界线(一个由'--'开始的字符串)划分, 也是一种请求的媒体类型MIME

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

    Check

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

    3.照葫芦画瓢

    以上就是常规的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 服务端

    上传文件的代码在 《》一文已经体现,本次截取接收文件上传的核心代码

     [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(多部分表单媒体类型)发起上传请求。

    本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/15697845.html

    欢迎关注我的原创技术、职场公众号, 加好友谈天说地,一起进化
    上海鲜花港 - 郁金香
  • 相关阅读:
    centos 7.1开机/etc/rc.local脚本不执行的问题
    ssh免密码登录之ssh-keygen的用法
    Centos 7.x临时的网络与路由配置
    Centos 7.x系统安装后的初始化配置
    U盘安装Centos7.1操作系统的问题记录
    linux系统中关于shell变量$*与$@的区别
    linux服务器init 5启动图形界面,报错Retrigger failed udev events
    rpmdb: unable to join the environment的问题解决
    BFS 典型的迷宫问题
    JUnit的基本使用
  • 原文地址:https://www.cnblogs.com/JulianHuang/p/15697845.html
Copyright © 2011-2022 走看看