.Net Web API 005 Controller上传小文件
一般情况下,系统里面的文件都会附属一个对象存在,例如用户的头像文件,会附属用户对象存在。邮件中的文件会附属邮件存在。所以在系统里面,我们会创建一个附属文件对象,命名为AttachedFileEntity。其定义如下所示。
/// <summary> /// 附属文件实体对象 /// </summary> public class AttachedFileEntity { /// <summary> /// 实体对象GUID /// </summary> public string GUID { get; set; } = ""; /// <summary> /// 所属对象的GUID /// </summary> public string EntityGUID { get; set; } = ""; /// <summary> /// 名称 /// </summary> public string Name { get; set; } = ""; /// <summary> /// 关键字 /// </summary> public string KeyWord { get; set; } = ""; /// <summary> /// 文件大小 /// </summary> public int FileSize { get; set; } = 0; /// <summary> /// 服务器存储路径 /// </summary> public string ServerPath { get; set; } = ""; /// <summary> /// 描述信息 /// </summary> public string Description { get; set; } = ""; }
EntityGUID属性的作用是,定义该文件属于哪个实体对象,例如某个用户的头像文件,该属性就是这个用户对象的GUID值。
KeyWord属性用来标识文件。例如UserEntity有两个文件,头像和一个自我介绍的视频文件。这两个文件的EntityGUID都是UserEntity的GUID,那么就可以通过KeyWord来区分两个文件是做什么用的。
如果一个文件比较小,例如3M以内,那么我们就可以一次性把文件上传上来,上传的时候,要把AttachedFileEntity对象传进来,并添加到数据库中。
/// <summary> /// 上传文件 /// </summary> /// <param name="pEntity"></param> /// <returns></returns> [HttpPost] [Route("UploadFile")] public IActionResult UploadFile() { //获取客户端传来的数据 var myEntityJosnString = Request.Form["pEntity"].ToString(); var myEntity = JsonSerializer.Deserialize<AttachedFileEntity>(myEntityJosnString); var myFile = Request.Form.Files[0]; //设置新的文件路径 string myFileEx = Path.GetExtension(myFile.FileName); string myServerFilePath = DateTime.Now.ToString("yyyy_MM_dd") + "\\" + Guid.NewGuid().ToString() + myFileEx; myEntity!.ServerPath = myServerFilePath; //创建目录 string myFullServerPath = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\" + myServerFilePath; string myFullFolder = Path.GetDirectoryName(myFullServerPath)!; if (Directory.Exists(myFullFolder) == false) { Directory.CreateDirectory(myFullFolder); } Stream? myStream = null; FileStream? myFileStream = null; BinaryWriter? myBinaryWriter = null; try { myStream = myFile.OpenReadStream(); byte[] myBytes = new byte[myStream.Length]; myStream.Read(myBytes, 0, myBytes.Length); myStream.Seek(0, SeekOrigin.Begin); myFileStream = new FileStream(myFullServerPath, FileMode.Create); myBinaryWriter = new BinaryWriter(myFileStream); myBinaryWriter.Write(myBytes); } finally { myBinaryWriter?.Close(); myFileStream?.Close(); myStream?.Close(); } //把附属文件对象保存到数据库中 //代码略 return this.Ok(myEntity); }
因为我们要传入两个复杂的对象AttachedFileEntity和File,所以就不能用参数接了,就需要用代码从Request里面读取。文件其本质就是二进制数据,我们获取这个二进制之后,把数据保存成文件就可以了。然后把pEntity写入到数据库中。
先用桌面端测试,界面是用C#写的WPF桌面软件,入下图所示。
var myFilePath = this.UI_SmallFile_TextBox.Text.Trim(); if (myFilePath.Length == 0) { MessageBox.Show("请选择一个文件。"); return; } if (File.Exists(myFilePath) == false) { MessageBox.Show("文件不存在,请重新选择。"); return; } //定义AttachedFileEntity var myAttachedFileEntity = new AttachedFileEntity() { GUID = Guid.NewGuid().ToString(), Name = "用户头像", KeyWord = "UserProfilePhoto", Description = "", EntityGUID = "AAAA" }; //定义请求内容 var myFileStream = new FileStream(myFilePath, FileMode.Open); myAttachedFileEntity.FileSize = (int)myFileStream.Length; var myFileName = Path.GetFileName(myFilePath); var myFileStreamContent = new StreamContent(myFileStream); var myMultipartFormDataContent = new MultipartFormDataContent { { JsonContent.Create(myAttachedFileEntity), "pEntity" }, { myFileStreamContent, "pFormFile", myFileName } }; //请求服务 var myHttpClientEx = new HttpClientEx(new HttpClient()) { Url = "http://localhost:5000/api/AttachedFile/UploadFile", HttpContent = myMultipartFormDataContent }; await myHttpClientEx.PostAsync(); myFileStream.Close(); //解析结果 if (myHttpClientEx.IsSuccess == false) { MessageBox.Show(("上传文件失败," + myHttpClientEx.ResponseContenString)); return; } var myEntity = myHttpClientEx.GetResponseObject<AttachedFileEntity>(); var myEntityJosnString = JsonSerializer.Serialize(myEntity); MessageBox.Show(myEntityJosnString);
HttpClientEx是对.Net定义的HttpClient一些功能的扩展,这样用起来会比较方便,代码定义如下。
/// <summary> /// HttpClient的自定义扩展类 /// </summary> public class HttpClientEx { /// <summary> /// HttpClient的自定义扩展类 /// </summary> /// <param name="pHttpClient"></param> public HttpClientEx(HttpClient? pHttpClient) { this.HttpClient = pHttpClient; this.ParameterDictionary = new Dictionary<string, string>(); } /// <summary> /// HttpClient对象 /// </summary> public HttpClient? HttpClient { get; private set; } /// <summary> /// 服务地址 /// </summary> public string Url { get; set; } = ""; /// <summary> /// 参数字典 /// </summary> public Dictionary<string, string> ParameterDictionary { get; private set; } /// <summary> /// 请求内容 /// </summary> public HttpContent? HttpContent { get; set; } /// <summary> /// 请求返回的消息 /// </summary> public HttpResponseMessage? ResponseMessage { get; private set; } /// <summary> /// 是否执行成功 /// </summary> public bool IsSuccess { get; private set; } /// <summary> /// 返回的内容字符串 /// </summary> public string ResponseContenString { get; private set; } = ""; /// <summary> /// Get /// </summary> /// <returns></returns> public async Task GetAsync() { var myUrlWithParameters = this.GetUrlWithParameters(); this.ResponseMessage = await this.HttpClient!.GetAsync(myUrlWithParameters); this.IsSuccess = this.ResponseMessage.IsSuccessStatusCode; this.ResponseContenString = await this.ResponseMessage.Content.ReadAsStringAsync(); } /// <summary> /// Get /// </summary> /// <returns></returns> public async Task PostAsync() { var myUrlWithParameters = this.GetUrlWithParameters(); this.ResponseMessage = await this.HttpClient!.PostAsync(myUrlWithParameters, this.HttpContent); this.IsSuccess = this.ResponseMessage.IsSuccessStatusCode; this.ResponseContenString = await this.ResponseMessage.Content.ReadAsStringAsync(); } /// <summary> /// 得到返回的对象 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public T? GetResponseObject<T>() { if (this.ResponseContenString == "") { return default; } var myJsonSerializerOptions = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }; return JsonSerializer.Deserialize<T>(this.ResponseContenString, myJsonSerializerOptions); } /// <summary> /// 得到带参数的Url /// </summary> /// <returns></returns> private string GetUrlWithParameters() { if (this.ParameterDictionary == null) { return this.Url; } if (this.ParameterDictionary.Count == 0) { return this.Url; } var myParameterList = new List<string>(); foreach (var myItem in this.ParameterDictionary) { myParameterList.Add(myItem.Key + "=" + myItem.Value); } return this.Url + "?" + string.Join("&", myParameterList); } }
如果客户端是Js,就需要自己组织服务需要的数据了。代码入下所示。
var myFileReader = new FileReader(); var myFileName = ""; myFileReader.onloadend = function () { var myFileResult = myFileReader.result; var myFileLength = myFileResult.byteLength; var myFileEntity = new Object() { ServerPath: "" }; Upload(); function Upload() { var myByteArray = myFileResult.slice(0, myFileLength); var myBlob = new Blob([myByteArray]); var myFile = new File([myBlob], myFileName); var myFormData = new FormData(); myFormData.append("file", myFile) myFormData.append("pEntity", json.stringify(myFileEntity)); request.post(myUrl, { data: myFormData }).then(function (data) { myFileEntity = json.parse(data); alert("上传文件结束。"); alert(json.stringify(myFileEntity)); }, function (err) { alert(err); return; }); } } myFileName = this.files[0].name; myFileReader.readAsArrayBuffer(this.files[0]);