一般情況下,系統裏面的文件都會附屬一個對象存在,例如用戶的頭像文件,會附屬用戶對象存在。郵件中的文件會附屬郵件存在。所以在系統裏面,我們會創建一個附屬文件對象,命名爲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]);