经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
.Net Web API 006 Controller上传大文件
来源:cnblogs  作者:mytudousi  时间:2023/8/9 9:16:42  对本文有异议

1、上传大文件的方式

上传大文件就需要一段一段的上传,主要是先在客户端获取文件的大小,例如想一次传256kb,那就按照256kb分割。分割后又两种上传方式。

(1)逐个数据段读取,然后调用API上传,把数据追加到文件上。上传完这一段,接着传下一段,直到上传完毕。

(2)方式与(1)类似,只是可以好几段可以并行上传,上传后,会把每段按照索引命名,在服务器上保存成临时文件。判断都上传完毕后,再调用合并命令,把这些小的碎文件,合并成大的目标文件。

我一般喜欢用第一种方式,主要是因为简单明了。缺点就是,因为必须等上一段完成后,再传下一段,所以无法并行上传,导致速度会受到影响。

但我参与的项目基本上都是在局域网运行并且使用人数有限,所以这种传大文件的方式是可以满足系统要求。

2、上传大文件API实现

下面的实现用到了定义的AttachedFileEntity和HttpClientEx,这两个类的定义可参考 005 Controller上传小文件

分三步,开始上传、循环上传二进制段、结束上传。

开始上传API的目的是为了获取文件在服务器上的存储路径,代码如下。

  1. /// <summary>
  2. /// 开始大上传文件
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpPost]
  6. [Route("StartUploadBigFile")]
  7. public IActionResult StartUploadBigFile(AttachedFileEntity pEntity, string pFileEx)
  8. {
  9. string myServerFilePath = DateTime.Now.ToString("yyyy_MM_dd") + "\\" + Guid.NewGuid().ToString() + pFileEx;
  10. pEntity.ServerPath = myServerFilePath;
  11. return this.Ok(pEntity);
  12. }

分段上传文件,接收到后,追加到现有的文件上。

  1. /// <summary>
  2. /// 上传大文件
  3. /// </summary>
  4. /// <param name="pServerPath"></param>
  5. /// <returns></returns>
  6. [HttpPost]
  7. [Route("UploadBigFile")]
  8. [DisableRequestSizeLimit]
  9. public IActionResult UploadBigFile(string pServerPath)
  10. {
  11. var myFile = Request.Form.Files[0];
  12. //创建目录
  13. string myFullServerPath = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\" + pServerPath;
  14. string myFullFolder = Path.GetDirectoryName(myFullServerPath)!;
  15. if (Directory.Exists(myFullFolder) == false)
  16. {
  17. Directory.CreateDirectory(myFullFolder);
  18. }
  19. //写入文件
  20. Stream? myStream = null;
  21. FileStream? myFileStream = null;
  22. BinaryWriter? myBinaryWriter = null;
  23. try
  24. {
  25. myStream = myFile.OpenReadStream();
  26. byte[] myBytes = new byte[myStream.Length];
  27. myStream.Read(myBytes, 0, myBytes.Length);
  28. myStream.Seek(0, SeekOrigin.Begin);
  29. myFileStream = new FileStream(myFullServerPath, FileMode.Append);
  30. myBinaryWriter = new BinaryWriter(myFileStream);
  31. myBinaryWriter.Write(myBytes);
  32. }
  33. catch (Exception ex)
  34. {
  35. return this.BadRequest("上传大文件失败," + ex.Message);
  36. }
  37. finally
  38. {
  39. myBinaryWriter?.Close();
  40. myFileStream?.Close();
  41. myStream?.Close();
  42. }
  43. return this.Ok();
  44. }

 

最后,结束上传,把文件的信息记录到数据库中。

  1. /// <summary>
  2. /// 结束上传大文件
  3. /// </summary>
  4. /// <returns></returns>
  5. [HttpPost]
  6. [Route("FinishUploadBigFile")]
  7. public IActionResult FinishUploadBigFile(AttachedFileEntity pEntity)
  8. {
  9. if (string.IsNullOrEmpty(pEntity.GUID))
  10. {
  11. pEntity.GUID = Guid.NewGuid().ToString();
  12. }
  13. //记录到数据库中
  14. //代码略
  15. return this.Ok(pEntity);
  16. }

3、客户端调用

客户端如果用的C#,代码中没有加入进度信息,进度信息可以传入一个ProcessInfo对象,传一段数据后,就更新下进度信息。

调用的代码入下所示。

  1. private void Init_BigFileUpLoad_UIs()
  2. {
  3. this.UI_BigFile_Button.Click += (x, y) =>
  4. {
  5. var myOpenFileDialog = new OpenFileDialog
  6. {
  7. Filter = ".*|*.*"
  8. };
  9. var myIsOK = myOpenFileDialog.ShowDialog();
  10. if (myIsOK != true)
  11. {
  12. return;
  13. }
  14. this.UI_BigFile_TextBox.Text = myOpenFileDialog.FileName;
  15. };
  16. this.UI_BigFileUpLoad_Button.Click += async (x, y) =>
  17. {
  18. var myFilePath = this.UI_BigFile_TextBox.Text.Trim();
  19. if (myFilePath.Length == 0)
  20. {
  21. MessageBox.Show("请选择一个文件。");
  22. return;
  23. }
  24. if (File.Exists(myFilePath) == false)
  25. {
  26. MessageBox.Show("文件不存在,请重新选择。");
  27. return;
  28. }
  29. //定义AttachedFileEntity
  30. var myFileEntity = new AttachedFileEntity()
  31. {
  32. GUID = Guid.NewGuid().ToString(),
  33. Name = "用户头像",
  34. KeyWord = "UserProfilePhoto",
  35. Description = "",
  36. EntityGUID = "AAAA"
  37. };
  38. //打开上传的文件
  39. var myFileStream = new FileStream(myFilePath, FileMode.Open);
  40. myFileEntity.FileSize = (int)myFileStream.Length;
  41. var myFileName = Path.GetFileName(myFilePath);
  42. //每次上传256kb
  43. int myChunkSize = 1024 * 256;
  44. int myChunkCount = (int)Math.Ceiling(myFileStream.Length / (double)myChunkSize);
  45. //调用开始上传
  46. var myHttpClient = new HttpClient();
  47. var myHttpClientEx = new HttpClientEx(myHttpClient)
  48. {
  49. Url = "http://localhost:5000/api/AttachedFile/StartUploadBigFile",
  50. HttpContent = JsonContent.Create(myFileEntity)
  51. };
  52. myHttpClientEx.ParameterDictionary.Add("pFileEx", Path.GetExtension(myFileName));
  53. await myHttpClientEx.PostAsync();
  54. if (myHttpClientEx.IsSuccess == false)
  55. {
  56. myFileStream.Close();
  57. MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);
  58. return;
  59. }
  60. myFileEntity = myHttpClientEx.GetResponseObject<AttachedFileEntity>();
  61. if (myFileEntity == null)
  62. {
  63. myFileStream.Close();
  64. MessageBox.Show("上传文件失败,返回的AttachedFileEntity为null。");
  65. return;
  66. }
  67. //循环上传文件
  68. for (int i = 0; i < myChunkCount; i++)
  69. {
  70. //组织数据
  71. int myByteArraySize = myChunkSize;
  72. if (i == myChunkCount - 1)
  73. {
  74. myByteArraySize = (int)(myFileStream.Length % myChunkSize);
  75. }
  76. byte[] myBytes = new byte[myByteArraySize];
  77. myFileStream.Position = myChunkSize * i;
  78. myFileStream.Read(myBytes, 0, myBytes.Length);
  79. var myMemoryStream = new MemoryStream(myBytes);
  80. //请求服务
  81. myHttpClientEx = new HttpClientEx(myHttpClient)
  82. {
  83. Url = "http://localhost:5000/api/AttachedFile/UploadBigFile",
  84. HttpContent = new MultipartFormDataContent
  85. {
  86. {new StreamContent(myMemoryStream),"pFile",myFileName}
  87. }
  88. };
  89. myHttpClientEx.ParameterDictionary.Add("pServerPath", myFileEntity!.ServerPath);
  90. await myHttpClientEx.PostAsync();
  91. //解析结果
  92. if (myHttpClientEx.IsSuccess == false)
  93. {
  94. myFileStream.Close();
  95. MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);
  96. return;
  97. }
  98. }
  99. //结束上传
  100. myHttpClientEx = new HttpClientEx(myHttpClient)
  101. {
  102. Url = "http://localhost:5000/api/AttachedFile/FinishUploadBigFile",
  103. HttpContent = JsonContent.Create<AttachedFileEntity>(myFileEntity)
  104. };
  105. await myHttpClientEx.PostAsync();
  106. if (myHttpClientEx.IsSuccess == false)
  107. {
  108. myFileStream.Close();
  109. MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);
  110. return;
  111. }
  112. myFileStream.Close();
  113. myFileEntity = myHttpClientEx.GetResponseObject<AttachedFileEntity>();
  114. var myEntityJosnString = JsonSerializer.Serialize<AttachedFileEntity>(myFileEntity);
  115. MessageBox.Show(myEntityJosnString);
  116. };
  117. }

如果客户端是js,代码如下。

  1. on(myButton, "change", function (e) {
  2. var myFileReader = new FileReader();
  3. var myFileName = "";
  4. myFileReader.onloadend = function () {
  5. var myFileResult = myFileReader.result;
  6. var myFileLength = myFileResult.byteLength;
  7. var myPerLength = 1024 * 256;
  8. var myCount = Math.ceil(myFileLength / myPerLength);
  9. var myFileEntity = new Object()
  10. {
  11. ServerPath: ""
  12. };
  13. //调用开始上传StartUploadBigFile,具体代码略。
  14.  
  15. var myK = 0;
  16. Upload();
  17. function Upload() {
  18. var myByteArray = myFileResult.slice(myPerLength * myK, myPerLength * (myK + 1));
  19. var myBlob = new Blob([myByteArray]);
  20. var myFile = new File([myBlob], myFileName);
  21. var myFormData = new FormData();
  22. myFormData.append("file", myFile)
  23. request.post(myUrl + "?pServerFile=" + myFileEntity.ServerPath +, {
  24. data: myFormData
  25. }).then(function (data) {
  26. myFileEntity = json.parse(data);
  27. myK++;
  28. if (myK < myCount) {
  29. Upload();
  30. }
  31. else {
  32. alert("上传大文件结束。");
  33. alert(json.stringify(myFileEntity));
  34. //结束,post FinishUploadBigFile
  35. }
  36. }, function (err) {
  37. alert(err);
  38. return;
  39. });
  40. }
  41. }
  42. myFileName = this.files[0].name;
  43. myFileReader.readAsArrayBuffer(this.files[0]);
  44. });

Js代码没有实际测试,只是一个思路。

原文链接:https://www.cnblogs.com/mytudousi/p/17611540.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号