1、附属文件对象定义
一般情况下,系统里面的文件都会附属一个对象存在,例如用户的头像文件,会附属用户对象存在。邮件中的文件会附属邮件存在。所以在系统里面,我们会创建一个附属文件对象,命名为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来区分两个文件是做什么用的。
2、小文件上传服务
如果一个文件比较小,例如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写入到数据库中。
3、前端调用
先用桌面端测试,界面是用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一些功能的扩展,这样用起来会比较方便,代码定义如下。
如果客户端是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]);