需求背景:

   在需要通过服务端请求传递文件二进制文件流数据到相关的服务端保存时,如对接第三方接口很多情况下都会提供一个上传文件的接口,但是当你直接通过前端Ajax的方式将文件流上传到对方提供的接口的时候往往都会存在跨域的情况,这时候我们就需要通过服务端提交文件流来解决这个跨域的情况。本篇的主角就是使用HttpClient进行Http请求,提交二进制文件流到文件服务器中。

HttpClient简单介绍:

HttpClient类实例充当发送 HTTP 请求的会话。 HttpClient实例是对该实例执行的所有请求应用的设置的集合。 此外,每个 HttpClient 实例都使用其自己的连接池,并从其他实例所执行的请求隔离其请求 HttpClient 。

使用注意点:HttpClient对象比较特殊,虽然继承了IDisposable这个接口但是它可以被共享实例,并且使用完不能立即关闭连接、性能消耗严重。所以我们在使用的时候,需要主动调用Dispose方法来释放它。可以使用using如下所示:

using(var client = new HttpClient())
{
//do something with http client
}

网上说.NET Core版本的HttpClient存在比较多的问题(不过我自己一直在使用HttpClient做一些http请求),大家也可以HttpClientFactory,ASP.NET Core中使用HttpClientFactory官方教程:

在 ASP.NET Core 中使用 IHttpClientFactory 发出 HTTP 请求

前端使用Ajax-FormData对象上传文件:

注意点:

FormData:对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。其主要用于发送表单数据,但亦可用于发送带键数据(keyed data),而独立于表单使用。

contentType:需设置为false,在Ajax中contentType 设置为false 是为了避免 JQuery 对其操作,从而失去分界符,而使服务器不能正常解析文件。

processData:需设置为false,默认为true,表示以对象的形式上传的时候会默认把对象转化为字符串的形式上传。

<div class="text-center">
<p><input type="file" id="imageFile" onchange="uploadImage(this)" /></p>
</div> <div id="imageBox"> </div> <script type="text/javascript">
//图片上传
function uploadImage(fileObject) {
var formData = new FormData();
var files = $(fileObject).prop('files'); //获取到文件列表【$("#imageFile").get(0)通过id获取文件列表】
formData.append("files", files[0]);//图片文件流
console.log('formData=>>>', formData, files);
$.ajax({
async: true,
url:"@Url.Action("UploadImage", "ImageFileManage")",
type: 'post',
data: formData,
//https://segmentfault.com/a/1190000007207128?utm_source=tag-newest
//在 ajax 中 contentType 设置为 false 是为了避免 JQuery 对其操作,从而失去分界符,而使服务器不能正常解析文件
contentType: false,
//告诉jQuery不要去处理发送的数据
processData: false,
success: function (res) {
console.log(res);
if (res.code == 0) {
$("#imageBox").append("<img width='100' height='100' src=" + res.msg.completeFilePath+">");
}
}
});
}
</script>

接收Ajax传递的文件流,并转化为转化字节类型:

    /// <summary>
/// 图片文件管理
/// </summary>
public class ImageFileManageController : Controller
{
/// <summary>
/// 接收Ajax传递的文件流
/// </summary>
/// <param name="files">表单文件信息</param>
/// <returns></returns>
public IActionResult UploadImage(IFormFile files)
{
//var files = Request.Form.Files[0];//获取请求发送过来的文件
if (files.Length <= 0)
return Json(new { code = 1, msg = "请选择需要上传的文件~" }); var fileBytes = ReadFileBytes(files);
var fileExtension = Path.GetExtension(files.FileName);//获取文件格式,拓展名
var result = HttpClientHelper._.HttpClientPost("https://localhost:44347/FileUpload/SingleFileUpload", fileBytes, fileExtension, files.FileName); var resultObj = JsonConvert.DeserializeObject<UploadReponse>(result);
if (resultObj.IsSuccess)
{
return Json(new { code = 0, msg = resultObj });
}
else
{
return Json(new { code = 1, msg = resultObj.ReturnMsg });
}
} /// <summary>
/// 文件流类型转化字节类型
/// </summary>
/// <param name="fileData">表单文件信息</param>
/// <returns></returns>
private byte[] ReadFileBytes(IFormFile fileData)
{
byte[] data;
using (Stream inputStream = fileData.OpenReadStream())//读取上传文件的请求流
{
MemoryStream memoryStream = inputStream as MemoryStream;
if (memoryStream == null)
{
memoryStream = new MemoryStream();
inputStream.CopyTo(memoryStream);
}
data = memoryStream.ToArray();
}
return data;
} } /// <summary>
/// 上传响应模型
/// </summary>
public class UploadReponse
{
/// <summary>
/// 是否成功
/// </summary>
public bool IsSuccess { get; set; } /// <summary>
/// 结果
/// </summary>
public string ReturnMsg { get; set; } /// <summary>
/// 完整地址
/// </summary>
public string CompleteFilePath { get; set; }
}

向目标地址提交图片文件参数数据(HttpClient-上传multipart/form-data内容类型):

注意:

    /// <summary>
/// Http网络请求帮助类
/// </summary>
public class HttpClientHelper
{
private static HttpClientHelper _httpClientHelper; public static HttpClientHelper _
{
get => _httpClientHelper ?? (_httpClientHelper = new HttpClientHelper());
set => _httpClientHelper = value;
} /// <summary>
/// 向目标地址提交图片文件参数数据
/// </summary>
/// <param name="requestUrl">请求地址</param>
/// <param name="bmpBytes">图片字节流</param>
/// <param name="imgType">上传图片类型</param>
/// <param name="fileName">图片名称</param>
/// <returns></returns>
public string HttpClientPost(string requestUrl, byte[] bmpBytes, string imgType, string fileName)
{
using (var httpClient = new HttpClient())
{
List<ByteArrayContent> byteArrayContents = new List<ByteArrayContent>(); var imgTypeContent = new ByteArrayContent(Encoding.UTF8.GetBytes(imgType));
imgTypeContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "imgType"
};
byteArrayContents.Add(imgTypeContent); var fileContent = new ByteArrayContent(bmpBytes);//填充图片文件二进制字节
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "file",
FileName = fileName
};
byteArrayContents.Add(fileContent); var content = new MultipartFormDataContent();
//将ByteArrayContent集合加入到MultipartFormDataContent中
foreach (var byteArrayContent in byteArrayContents)
{
content.Add(byteArrayContent);
} try
{
var result = httpClient.PostAsync(requestUrl, content).Result;//post请求
return result.Content.ReadAsStringAsync().Result;
}
catch (Exception ex)
{
return ex.Message;
}
}
}
}

模拟第三方上传文件接口,保存图片到服务端并返回文件预览完整地址:

关于.NET Core上传文件的后端服务接口可以参考我之前写过的文章:

ASP.NET Core单文件和多文件上传并保存到服务端

        /// <summary>
/// 单文件上传(Ajax,Form表单都适用)模拟第三方服务端接口
/// </summary>
/// <param name="file">表单文件信息</param>
/// <returns></returns>
public JsonResult SingleFileUpload(IFormFile file)
{
try
{
if (file != null)
{
var currentDate = DateTime.Now;
var webRootPath = _hostingEnvironment.WebRootPath;//>>>相当于HttpContext.Current.Server.MapPath("") var filePath = $"/UploadFile/{currentDate:yyyyMMdd}/"; //创建每日存储文件夹
if (!Directory.Exists(webRootPath + filePath))
{
Directory.CreateDirectory(webRootPath + filePath);
} //文件后缀
var fileExtension = Path.GetExtension(file.FileName);//获取文件格式,拓展名 //判断文件大小
var fileSize = file.Length; if (fileSize > 1024 * 1024 * 10) //10M TODO:(1mb=1024X1024b)
{
return Json(new { isSuccess = false, resultMsg = "上传的文件不能大于10M" });
} //保存的文件名称(以名称和保存时间命名)
var saveName = file.FileName.Substring(0, file.FileName.LastIndexOf('.')) + "_" + currentDate.ToString("HHmmss") + fileExtension; //文件保存
using (var fs = System.IO.File.Create(webRootPath + filePath + saveName))
{
file.CopyTo(fs);
fs.Flush();
} //完整的文件路径
var completeFilePath = Path.Combine(filePath, saveName); return Json(new { isSuccess = true, returnMsg = "上传成功", completeFilePath = completeFilePath });
}
else
{
return Json(new { isSuccess = false, resultMsg = "上传失败,未检测上传的文件信息~" });
} }
catch (Exception ex)
{
return Json(new { isSuccess = false, resultMsg = "文件保存失败,异常信息为:" + ex.Message });
}
}

项目完整示例:

https://github.com/YSGStudyHards/DailyLearning

参考文章:

https://www.cnblogs.com/willick/p/net-core-httpclient.html

https://docs.microsoft.com/zh-cn/dotnet/api/system.net.http.httpclient?view=net-5.0

https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.http.iformfile.openreadstream?view=aspnetcore-5.0#Microsoft_AspNetCore_Http_IFormFile_OpenReadStream

.NET Core Web API使用HttpClient提交文件的二进制流(multipart/form-data内容类型)的更多相关文章

  1. 一个简单的QQ隐藏图生成算法 通过jQuery和C#分别实现对.NET Core Web Api的访问以及文件上传

    一个简单的QQ隐藏图生成算法   隐藏图不是什么新鲜的东西,具体表现在大部分社交软件中,预览图看到的是一张图,而点开后看到的又是另一张图.虽然很早就看到过这类图片,但是一直没有仔细研究过它的原理,今天 ...

  2. .net core web api 与httpclient发送和接收文件及数据

    客户端 HttpClient var url = $"https://localhost:44323/api/values/posttest?resource_source=yangwwme ...

  3. 通过jQuery和C#分别实现对.NET Core Web Api的访问以及文件上传

    准备工作: 建立.NET Core Web Api项目 新建一个用于Api请求的UserInfo类 public class UserInfo { public string name { get; ...

  4. 循序渐进学.Net Core Web Api开发系列【5】:文件上传

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍通 ...

  5. .NET和.NET Core Web APi FormData多文件上传对比

    前言 最近因维护.NET和.NET Core项目用到文件上传功能,虽说也做过,但是没做过什么对比,借此将二者利用Ajax通过FormData上传文件做一个总结,通过视图提交表单太简单,这里不做阐述,希 ...

  6. .NET Core Web APi大文件分片上传研究

    前言 前两天发表利用FormData进行文件上传,然后有人问要是大文件几个G上传怎么搞,常见的不就是分片再搞下断点续传,动动手差不多也能搞出来,只不过要深入的话,考虑的东西还是很多.由于断点续传之前写 ...

  7. ASP.NET Core WEB API 使用element-ui文件上传组件el-upload执行手动文件文件,并在文件上传后清空文件

    前言: 从开始学习Vue到使用element-ui-admin已经有将近快两年的时间了,在之前的开发中使用element-ui上传组件el-upload都是直接使用文件选取后立即选择上传,今天刚好做了 ...

  8. 在Mac下创建ASP.NET Core Web API

    在Mac下创建ASP.NET Core Web API 这系列文章是参考了.NET Core文档和源码,可能有人要问,直接看官方的英文文档不就可以了吗,为什么还要写这些文章呢? 原因如下: 官方文档涉 ...

  9. Gitlab CI 自动部署 asp.net core web api 到Docker容器

    为什么要写这个? 在一个系统长大的过程中会经历不断重构升级来满足商业的需求,而一个严谨的商业系统需要高效.稳定.可扩展,有时候还不得不考虑成本的问题.我希望能找到比较完整的开源解决方案来解决持续集成. ...

随机推荐

  1. 1151 LCA in a Binary Tree (30point(s))

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  2. linux删除文件空间不释放问题解决

    目录 场景描述 原因 解决方法 在线清空文件 场景描述 某天,收到Prometheus报警,生产中某台机器出现磁盘空间不足报警,该台服务器是mysql其中一台从库,远程登录到该服务后,排查磁盘空间的原 ...

  3. 【Redis连接超时】记录线上RedisConnectionFailureException异常排查过程

    项目架构: 部分组件如下: SpringCloudAlibaba(Nacos+Gateway+OpenFeign)+SpringBoot2.x+Redis 问题背景: 最近由于用户量增大,在高峰时期, ...

  4. 【MySQL】若sql语句中order by指定了多个字段,则怎么排序?

    举个例子吧:order by id desc,time desc先是按 id 降序排列 (优先)如果 id 字段 有些是一样的话 再按time 降序排列 (前提是满足id降序排列)   order b ...

  5. PHP正则匹配各种匹配方法

    平时做网站经常要用正则表达式,下面是一些讲解和例子,仅供大家参考和修改使用: 匹配数字 "^\d+$" //非负整数(正整数 + 0) "[1][1-9][0-9]$&q ...

  6. ARM汇编中的:比较指令--CMN / CMP / TEQ / TST

    1. 简介 CMP / CMN : 算术指令 TEQ / TST : 逻辑指令      它们总是会影响CPSR条件标志位. APSR(CPSR)与condition的关系图: 2. CMN -- 比 ...

  7. Day006 什么是方法

    什么是方法? ​ 方法是语句的集合,他们在一起执行一个功能. 方法是解决一类问题的步骤的有序集合. 方法包含于类和对象中. 方法在程序中被创建,在其他地方被引用. 设计方法的原则 方法的本意是功能块, ...

  8. [C#] 使用 NAudio 实现音频可视化

    预览: 捕捉声卡输出: 实现音频可视化, 第一步就是获得音频采样, 这里我们选择使用计算机正在播放的音频作为采样源进行处理: NAudio 中, 可以借助 WasapiLoopbackCapture ...

  9. 【DB宝51】CentOS7修改网卡名称

    目录 1.修改/etc/default/grub文件 2.修改/etc/udev/rules.d/70-persistent-net.rules文件 3.修改网卡配置文件 4.重启服务器 需求:原来的 ...

  10. 什么是 Mock 测试?

    什么是 Mock? 作为动词,Mock 是模拟.模仿的意思. 作为名词,Mock 是能够模仿真实对象行为的模拟对象. 那么,在软件测试中,Mock 所模拟的对象是什么呢? 模拟的是 SUT(Syste ...