- A+
所属分类:.NET技术
有时候,为了追求便利性,我们可能会让前端直接将文件上传到阿里云OSS,然后将URL提交给ASP.NET。然而,这种做法意味着前端需要拥有OSS的访问密钥,而将密钥存放在前端,无疑增加了被破解的风险。因此,最安全的做法仍然是由服务器端负责上传文件到OSS。
接下来,我将演示如何实现分块上传到服务器的过程,而且在这个过程中,服务器并不保存任何分块,而是直接将分块上传到OSS。
服务器端
asp.net 引用 nuget 包:
JMS.FileUploader.AspNetCore
Aliyun.OSS.SDK.NetCore
实现一个oss的 IUploadFilter , 把接收到的分块数据,实时传到oss
[UploadFilterDescription("Aliyun")] public class AliyunUploadFilter : IUploadFilter { const string BucketName = "<your-bucket-name>"; const string OssEndpoint = "<your-oss-endpoint>"; const string AccessKeyId = "<your-accessKeyId>"; const string AccessKeySecret = "<your-accessKeySecret>"; string _uploadId; string _ossUploadId; string _objectKey; OssClient _ossClient; public async Task OnUploadBeginAsync(HttpContext context, string uploadId, string fileName, long fileSize, int fileItemIndex) { _uploadId = uploadId; _objectKey = $"file{fileItemIndex}.zip"; _ossClient = new OssClient(OssEndpoint , AccessKeyId , AccessKeySecret ); var ret = _ossClient.InitiateMultipartUpload(new InitiateMultipartUploadRequest(BucketName, _objectKey)); if (ret.HttpStatusCode != System.Net.HttpStatusCode.OK) throw new Exception(ret.HttpStatusCode.ToString()); _ossUploadId = ret.UploadId; } public async Task OnReceivedAsync(HttpContext context, Stream inputStream, long position, int size) { var data = new byte[size]; await inputStream.ReadAtLeastAsync(data, size); using var ms = new MemoryStream(data); var num = (int)(position / 102400) + 1; var ret = _ossClient.UploadPart(new UploadPartRequest(BucketName, _objectKey, _ossUploadId) { InputStream = ms, PartSize = size, PartNumber = num }); if (ret.HttpStatusCode != System.Net.HttpStatusCode.OK) throw new Exception(ret.HttpStatusCode.ToString()); } public async Task<string> OnUploadCompletedAsync(HttpContext context) { for (int i = 0; i < 3; i++) // 如果发生错误,最多尝试3次 { try { // 列出所有分块。 var listPartsRequest = new ListPartsRequest(BucketName, _objectKey, _ossUploadId); var partList = _ossClient.ListParts(listPartsRequest); // 创建CompleteMultipartUploadRequest对象。 var completeRequest = new CompleteMultipartUploadRequest(BucketName, _objectKey, _ossUploadId); // 设置分块列表。 foreach (var part in partList.Parts) { completeRequest.PartETags.Add(new PartETag(part.PartNumber, part.ETag)); } // 完成上传。 var ret = _ossClient.CompleteMultipartUpload(completeRequest); if (ret.HttpStatusCode != System.Net.HttpStatusCode.OK) throw new Exception(ret.HttpStatusCode.ToString()); //设置访问权限 _ossClient.SetObjectAcl(BucketName, _objectKey, CannedAccessControlList.PublicRead); //返回下载的url路径 return ret.Location; } catch (Exception) { if (i == 2) { throw; } else { Thread.Sleep(3000); } } } return null; } public void OnUploadError() { } }
然后注册这个 filter :
services.AddFileUploadFilter<AliyunUploadFilter>();
启用上传组件:
app.UseJmsFileUploader();
controller里面写一个最终的业务处理函数
[ApiController] [Route("[controller]/[action]")] public class MainController : ControllerBase { [HttpPost] public string Test([FromBody] object body) { var customHeader = Request.Headers["Custom-Header"]; //临时文件路径 var filepath = Request.Headers["FilePath"]; //文件名 var filename = Request.Headers["Name"]; return filepath + "rn" + filename + "rn" + customHeader; } }
前端
前端 import 模块:jms-uploader
async function uploadToAliyun() { //自定义请求头 var headers = function () { return { "Custom-Header": "test" }; }; //提交的body var dataBody = { name: "abc" }; var uploader = new JmsUploader("http://localhost:5200/Main/Test", [document.querySelector("#file1").files, document.querySelector("#file2").files], headers, dataBody); uploader.setPartSize(1024*300);//设置分块大小300K uploader.setUploadFilter("Aliyun");//设置服务器使用哪个upload filter uploader.onUploading = function (percent, uploadedSize, totalSize) { document.querySelector("#info").innerHTML = percent + "% " + uploadedSize + "," + totalSize; }; try { var ret = await uploader.upload(); alert(ret); } catch (e) { alert("错误:" + JSON.stringify(e)); } }
html
<body> <input id="file1" multiple type="file" /> <input id="file2" multiple type="file" /> <button onclick="uploadToAliyun()"> upload to aliyun oss </button> <div id="info"></div> </body>