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);
}
}