.NET C# 使用System.Net.Http下HttpClient执行异步多文件上传任务

1.前提

System.Net.Http.HttpClient的使用及介绍,请参照此文档:https://www.cnblogs.com/jeff151013/p/14704576.html

2.Tools工具代码(直接上工具代码,其他辅助参数、变量、请参考上面的文档链接)

/// <summary>
        /// 执行多文件上传
        /// </summary>
        /// <param name="uri">请求的uri</param>
        /// <param name="methodType">暂时只支持POST方式</param>
        /// <param name="filePaths">多文件(全)路径列表</param>
        /// <param name="outputMediaType">期望服务器返回的结果数据类型,默认json</param>
        public static async Task<string> ExecuteFileUploadAsync(Uri uri, Enums.HttpMethodEnum methodType,
            List<string> filePaths, string outputMediaType = "json")
        {
            HttpResponseMessage result = null;
            HttpRequestMessage httpRequestMessage = null;
            MultipartFormDataContent multipartFormDataContent = null;
            try
            {
                HttpClient client = CreateHttpClient(outputMediaType);
                multipartFormDataContent = new MultipartFormDataContent();
                filePaths.ForEach(filePath =>
                {
                    var fileStream = new FileStream(filePath, FileMode.Open);
                    var fileName = Path.GetFileName(filePath);
                    multipartFormDataContent.Add(new StreamContent(fileStream), "file", fileName);
                });
                httpRequestMessage = new HttpRequestMessage()
                {
                    Method = HttpMethod.Post,//TODO 后续如果支持更多中methodType,再放开
                    RequestUri = uri,
                    Content = multipartFormDataContent
                };
                result = await client.SendAsync(httpRequestMessage);
            }
            catch (AggregateException ex)
            {
                #region 获取线程异常信息
                Exception firstException = null;
                if (ex.InnerExceptions != null && ex.InnerExceptions.Any())
                {
                    firstException = ex.InnerExceptions.First();
                    if (firstException.InnerException != null)
                        firstException = firstException.InnerException;
                }

                //Logger.LogException(firstException);//Nlogger记录异常日志
                #endregion 获取线程异常信息
                result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
                {
                    Content =
                        new StringContent(firstException != null
                            ? firstException.ToString()
                            : "Encountered an AggreggateException without any inner exceptions")
                };
            }
            catch (Exception ex)
            {
                //Logger.LogException(ex);//Nlogger记录异常日志
                result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
                {
                    Content =
                        new StringContent("Encountered an AggreggateException without any inner exceptions")
                };
            }

            return await result.Content.ReadAsStringAsync();
        }

3.客户端调用示例代码(以winform举例):

public partial class Form1 : Form
    {
        /// <summary>
        /// 用于暂存待上传的文件全路径列表
        /// </summary>
        private List<string> _fullFilePaths = new List<string>();
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// 页面选择待上传文件按钮时间
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelectFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = true;
            ofd.Title = "请选择待上传的文件";
            ofd.Filter = "word文件|*.doc;*.docx|excel文件|*.xls;*.xlsx|pdf文件|*.pdf|所有文件(*.*)|*.*";
            ofd.RestoreDirectory = true;
            var result = ofd.ShowDialog();
            if (result == DialogResult.OK)
            {
                _fullFilePaths = ofd.FileNames.ToList();
                _fullFilePaths.ForEach(x=>rTxt.Text += System.Environment.NewLine + x);
            }
        }

        /// <summary>
        /// 上传按钮点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUpload_Click(object sender, EventArgs e)
        {
            if (_fullFilePaths == null || !_fullFilePaths.Any()) return;

            Task.Run(async () =>
            {

//上传URL来自app.config的配置
                var r = await ApiHttpUtils.ExecuteFileUploadAsync(new Uri(SystemConfig.XbrlBackgroundInspectFileReceiveUrl),
                    HttpMethodEnum.Post, _fullFilePaths, "json");

//DataTransferExtensions是包装的Newtonsoft的JSON序列化及反序列化的工具类

//将服务端的响应信息反序列化为具体的业务实体数据
                var response = DataTransferExtensions.GetTransferData<Response<object>>(r);
                if (response.IsSuccess && response.ErrorCode == ((int) ResponseStatusEnum.Ok).ToString())
                {

//根据业务返回结果更新UI提示信息
                    this.Invoke(new Action(() =>
                    {
                        this.lblTips.Text = "上传成功";
                    }));
                }
            });
        }

        /// <summary>
        /// 取消按钮点击事件,暂未实现
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            MessageBox.Show("暂未实现");
        }
    }

4.服务端接收示例代码(以ASP.NET WebAPI举例):

using System.Net.Http;
using System.Net.Http.Headers;

using System.Threading.Tasks;
using System.Web.Http;

public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
    {

//rootPath指定文件暂存的目录路径
        public CustomMultipartFormDataStreamProvider(string rootPath) : base(rootPath)
        {
        }

        public CustomMultipartFormDataStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize)
        {
        }

        public override string GetLocalFileName(HttpContentHeaders header)//重写此方法,主要为了解决当上传文件重名时可能出现的问题
        {
            var fileName = header.ContentDisposition.FileName.Replace(""", string.Empty);
            if (File.Exists(Path.Combine(base.RootPath, fileName)))
            {
                //文件名如果已存在,则新文件名:yyyyMMddhhmmss_DateTimeTicks_原文件名(不含后缀名).后缀名
                var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.Combine(base.RootPath, fileName));
                var extension = Path.GetExtension(Path.Combine(base.RootPath, fileName));
                return $"{DateTime.Now.ToString("yyyyMMddhhmmss")}_{DateTime.Now.Ticks}_{fileNameWithoutExtension}{extension}";
            }
            return fileName;//文件名如果不存在,则文件名即上传文件名
        }
    }

public class InspectSwitchController : ApiController
    {

        private readonly string _inspectSwitchFileImportTempPath =
            SystemConfig.InspectSwitchFileImportTempPath;//配置文件信息来自Web.config配置

[HttpPost]
        public async Task<IHttpActionResult> UploadImportingFile()
        {
            //TODO 日志记录传参,可能因文件过大序列化到日志时发生报错

            var response = new Response<InspectSwitchViewModel<ForAddResultDto>>();
            if (!Request.Content.IsMimeMultipartContent())//不受支持的MimeMultipartContent类型
            {
                response = new Response<InspectSwitchViewModel<ForAddResultDto>>()
                {
                    ErrorMsg = "不受支持的文件类型",
                    ErrorCode = ((int)ResponseStatusEnum.ProgramExecuteOccurException).ToString()
                };
            }

            try
            {
                var streamProvider = new CustomMultipartFormDataStreamProvider(_inspectSwitchFileImportTempPath);
                await Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith((t) =>
                {
                    if (t.IsFaulted || t.IsCanceled)
                    {
                        response = new Response<InspectSwitchViewModel<ForAddResultDto>>
                        {
                            ErrorMsg = "上传过程中发生错误或上传任务被取消",
                            ErrorCode = ((int)ResponseStatusEnum.ProgramExecuteOccurException).ToString()
                        };
                    }

                    if (t.IsCompleted)
                    {
                        //上传已完成,将指定目录下的所有文件信息记录到数据库中
        //xxxx
                    }
                });
            }
            catch (Exception ex)
            {
                response = new Response<InspectSwitchViewModel<ForAddResultDto>>()
                {
                    ErrorMsg = ex.Message,
                    ErrorCode = ((int)ResponseStatusEnum.ProgramExecuteOccurException).ToString()
                };
                //Logger.LogException(ex);//Nlogger日志记录异常信息
            }

            return Ok(response);
        }

 }

原文地址:https://www.cnblogs.com/jeff151013/p/14722569.html