zoukankan      html  css  js  c++  java
  • Asp.Net Core上传字节数组多种方式研究

    Asp.Net Core上传字节数组的应用场景很常见,可用的技术方案也很多,小的文件也可以读取到内存再按字节数组上传,这么多方式究竟有什么区别?特意写一下代码,通过Fiddler抓包,看一下不同的技术方案上传的数据究竟有啥区别。

    主要研究这3种方案:

    1 表单文件

    Json字节数组

    3 字节流

    创建服务端Asp.Net Core项目

    服务端写了3个方法,分别对应3种传输方案,另外增加一个传输Json整型数组的方法,用于对比。

            /// <summary>
            /// 上传表单文件
            /// </summary>
            /// <param name="form"></param>
            /// <returns></returns>
            [ProducesResponseType(StatusCodes.Status200OK)]
            [HttpPost("PostFormFile")]
            public async Task<int> PostFormFile(IFormCollection form)
            {
                byte[] ary = new byte[0];
    
                var formFile = form.Files?[0];
    
                if (formFile?.Length > 0)
                {
                    using var ms = new MemoryStream();
    
                    await formFile.CopyToAsync(ms);
    
                    ary = ms.ToArray();
                }
    
                string msg = $"上传表单文件, 长度 ={ary.Length}{Environment.NewLine}";
                //msg += string.Join(", ", ary.Take(10));
                msg += Encoding.UTF8.GetString(ary);
    
                _logger.LogDebug(msg);
    
                return ary.Length;
            }
    
            /// <summary>
            /// 上传字节Json数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            [ProducesResponseType(StatusCodes.Status200OK)]
            [HttpPost("PostByteJson")]
            public async Task<int> PostByteJson(byte[] ary)
            {
                string msg = $"上传字节Json数组, 长度 ={ary.Length}{Environment.NewLine}";
                //msg += string.Join(", ", ary.Take(10));
                msg += Encoding.UTF8.GetString(ary);
    
                _logger.LogDebug(msg);
    
                await Task.CompletedTask;
    
                return ary.Length;
            }
    
            /// <summary>
            /// 上传整型Json数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            [ProducesResponseType(StatusCodes.Status200OK)]
            [HttpPost("PostIntJson")]
            public async Task<int> PostIntJson(int[] ary)
            {
                string msg = $"上传整型Json数组, 长度 ={ary.Length}{Environment.NewLine}";
                msg += string.Join(", ", ary.Take(10));
    
                _logger.LogDebug(msg);
    
                await Task.CompletedTask;
    
                return ary.Length;
            }
    
            /// <summary>
            /// 上传字节数组
            /// </summary>
            /// <returns></returns>
            [ProducesResponseType(StatusCodes.Status200OK)]
            [HttpPost("PostByteAry")]
            public async Task<int> PostByteAry()
            {
                using var ms = new MemoryStream();
    
                await Request.Body.CopyToAsync(ms);
    
                var ary = ms.ToArray();
    
                string msg = $"上传字节数组, 长度 ={ary.Length}{Environment.NewLine}";
                //msg += string.Join(", ", ary.Take(10));
                msg += Encoding.UTF8.GetString(ary);
    
                _logger.LogDebug(msg);
    
                await Task.CompletedTask;
    
                return ary.Length;
            }

    创建客户端项目

    客户端也对应写了4种通过HttpClient上传数据的方法。

    /// <summary>
        /// HttpClient上传数据
        /// </summary>
        public class UploadClient
        {
            private readonly HttpClient _client;
            private readonly ILogger<UploadClient> _logger;
    
            public UploadClient(HttpClient client, ILogger<UploadClient> logger)
            {
                _client = client;
                _logger = logger;
            }
    
            /// <summary>
            /// HttpClient上传表单文件
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            public async Task<bool> PostFormFileAsync(byte[] ary)
            {
                _logger.LogInformation("HttpClient上传表单文件...");
    
                var multipartFormDataContent = new MultipartFormDataContent();
                multipartFormDataContent.Add(new ByteArrayContent(ary), "file", "test.txt");
    
                var response = await _client.PostAsync("/api/Test/PostFormFile", multipartFormDataContent);
    
                return response.IsSuccessStatusCode;
            }
    
            /// <summary>
            /// HttpClient上传字节Json数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            public async Task<bool> PostByteJsonAsync(byte[] ary)
            {
                _logger.LogInformation("HttpClient上传字节Json数组...");
    
                string json = JsonConvert.SerializeObject(ary);
    
                var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
    
                var response = await _client.PostAsync("/api/Test/PostByteJson", stringContent);
    
                return response.IsSuccessStatusCode;
            }
    
            /// <summary>
            /// HttpClient上传整型Json数组
            /// </summary>
            /// <param name="intAry"></param>
            /// <returns></returns>
            public async Task<bool> PostIntJsonAsync(int[] intAry)
            {
                _logger.LogInformation("HttpClient上传整型Json数组...");
    
                string json = JsonConvert.SerializeObject(intAry);
    
                var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
    
                var response = await _client.PostAsync("/api/Test/PostIntJson", stringContent);
    
                return response.IsSuccessStatusCode;
            }
    
            /// <summary>
            /// HttpClient上传字节数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            public async Task<bool> PostByteAryAsync(byte[] ary)
            {
                _logger.LogInformation("HttpClient上传字节数组...");
    
                //两种参数类型发送抓包一样
                //var ms = new MemoryStream(ary);
                //var byteArrayContent = new StreamContent(ms);
                var byteArrayContent = new ByteArrayContent(ary);
    
                var response = await _client.PostAsync("/api/Test/PostByteAry", byteArrayContent);
    
                return response.IsSuccessStatusCode;
            }
        }

    运行测试

    通过Fiddler抓包对比。

    表单文件方式上传数据比较复杂,适合上传大文件,或者表单内容有键值对又有文件。

    POST http://localhost:5000/api/Test/PostFormFile HTTP/1.1

    Host: localhost:5000

    Content-Type: multipart/form-data; boundary="2bc9b8ca-4ddf-4d5a-b4d3-4337cd6b07d3"

    Content-Length: 223

    --2bc9b8ca-4ddf-4d5a-b4d3-4337cd6b07d3

    Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt

    客户端现在时间=2021/10/23 16:48:19 +08:00

    --2bc9b8ca-4ddf-4d5a-b4d3-4337cd6b07d3--

    Json字节数组方式上传数据比较简单,字节数组json被自动转换为base64编码,减少传输数据量。

    POST http://localhost:5000/api/Test/PostByteJson HTTP/1.1

    Host: localhost:5000

    Content-Type: application/json; charset=utf-8

    Content-Length: 66

    "5a6i5oi356uv546w5Zyo5pe26Ze0PTIwMjEvMTAvMjMgMTY6NDg6MTkgKzA4OjAw"

    json整型数组方式上传数据,可以看到参数仍然保留了json数组的格式,它是无法被转换为base64编码字符串的。

    POST http://localhost:5000/api/Test/PostIntJson HTTP/1.1

    Host: localhost:5000

    Content-Type: application/json; charset=utf-8

    Content-Length: 41

    [900,901,902,903,904,905,906,907,908,909]

    字节流方式上传数据最精简。

    POST http://localhost:5000/api/Test/PostByteAry HTTP/1.1

    Host: localhost:5000

    Content-Length: 48

    客户端现在时间=2021/10/23 16:48:19 +08:00

    增加Refit客户端上传对比测试

    Refit采用声明式定义Web Api的接口,它的参数类型跟HttpClient不太一样。

    首先声明4个函数接口。

        public interface ITestPostApi
        {
    
            /// <summary>
            /// 上传表单文件
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            [Multipart]
            [Post("/api/Test/PostFormFile")]
            Task<int> PostFormFile(ByteArrayPart ary);
    
            /// <summary>
            /// 上传字节Json数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            [Post("/api/Test/PostByteJson")]
            Task<int> PostByteJson(byte[] ary);
    
            /// <summary>
            /// 上传整型Json数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            [Post("/api/Test/PostIntJson")]
            Task<int> PostIntJson(int[] ary);
    
            /// <summary>
            /// 上传字节数组
            /// </summary>
            /// <param name="ary"></param>
            /// <returns></returns>
            [Post("/api/Test/PostByteAry")]
            //两种参数类型发送抓包一样
            //Task<int> PostByteAry(StreamContent ary);//传参StreamContent类型
            Task<int> PostByteAry(ByteArrayContent ary);//传参ByteArrayContent类型
    
        }

    然后在startup注入Refit接口

                        //注册ITestPostApi
                        services.AddRefitClient<ITestPostApi>()
                            .ConfigureHttpClient(c => c.BaseAddress = new Uri("http://localhost:5000"));

    实现4种方式上传数据。

    //测试Refit上传数据
            private async Task TestRefitUploadAsync(CancellationToken stoppingToken)
            {
                //上传表单文件
                _logger.LogInformation("Refit上传表单文件...");
                var byteArrayPart = new ByteArrayPart(value: ary, fileName: "testfile", name: "file");
                int result = await _testPostApi.PostFormFile(byteArrayPart);
    
                await Task.Delay(100, stoppingToken);
    
                //上传字节Json数组
                _logger.LogInformation("Refit上传字节Json数组...");
                result = await _testPostApi.PostByteJson(ary);
    
                await Task.Delay(100, stoppingToken);
    
                //上传整型Json数组
                _logger.LogInformation("Refit上传整型Json数组...");
                result = await _testPostApi.PostIntJson(intAry);
    
                await Task.Delay(100, stoppingToken);
    
                //上传字节数组
                _logger.LogInformation("Refit上传字节数组...");
                //result = await _testPostApi.PostByteAry(byteArrayPart);
    
                //Task<int> PostByteAry(StreamContent ary)
                //var ms = new MemoryStream(ary);
                //var streamContent = new StreamContent(ms);
                //result = await _testPostApi.PostByteAry(streamContent);
    
                //Task<int> PostByteAry(ByteArrayContent ary)
                var byteArrayContent = new ByteArrayContent(ary);
                result = await _testPostApi.PostByteAry(byteArrayContent);
    
                //两种参数类型发送抓包一样
                /*
                POST http://localhost:5000/api/Test/PostByteAry HTTP/1.1
                Host: localhost:5000
                Content-Length: 48
    
                客户端现在时间=2021/10/23 15:56:55 +08:00             
                */
    
                //}
            }

    运行测试,用Fiddler抓包,发现Refit上传数据跟HttpClient几乎完全一样。

    小结

    如果是简单的上传小型字节数据,可以用HttpClientByteArrayContent参数,或者RefitByteArrayContent参数。

    DEMO代码地址:woodsun/TestPostBytes - 码云 - 开源中国 (gitee.com)

  • 相关阅读:
    JQuery的摸索之路(一)
    C#操作excel(NPOI篇)
    Mschart学习(MSchart介绍)
    JQuery的摸索之路(二比较)
    Mschart学习(开篇)
    Mschart学习(官方demo共享)
    SQL进阶提升(疑惑篇order by)学习sql server2005 step by step(十一)
    一步一步学习C#(二C#基础)
    NickLee的学习
    Linux 命令学习一
  • 原文地址:https://www.cnblogs.com/sunnytrudeau/p/15449080.html
Copyright © 2011-2022 走看看