1. 程式人生 > 其它 >.NET6 WebApi 大檔案上傳——分片上傳

.NET6 WebApi 大檔案上傳——分片上傳


最近遇到大檔案上傳問題,網上參考後寫的小DEMO
WebApi程式碼
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
using Polly;
using UploadFile.Models;

namespace UploadFile.Controllers
{
[ApiController]
[Route("[controller]/[action]")]
public
class UploadController : ControllerBase { [EnableCors("AllowSpecificOrigin")] [HttpPost] public async Task<IActionResult> RuleUploadFile([FromQuery] SliceFileInfo file) { try { string path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Upload"); var files = Request.Form.Files;
var buffer = new byte[file.Size]; var fileName = file.Name; path = path + "//" + fileName + "//"; if (!System.IO.Directory.Exists(path)) { System.IO.Directory.CreateDirectory(path); } string filepath = path + "//" + file.Name + "^" + file.Number; using (var stream = new FileStream(filepath, FileMode.Append)) {
await files[0].CopyToAsync(stream); } var filesList = Directory.GetFiles(Path.GetDirectoryName(path)); //當順序號等於分片總數量 合併檔案 if ((file.Number + 1) == file.Count || filesList.Length == file.Count) { await MergeFile(file); } return this.Ok(); } catch (Exception ex) { return BadRequest(ex.Message); } } /// <summary> /// 合併檔案 /// </summary> /// <param name="file"></param> /// <returns></returns> private async Task MergeFile(SliceFileInfo file) { string path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Upload"); var fileName = file.Name; path = path + "//" + fileName + "//"; string baseFileName = path + fileName.Split("~")[0].ToString(); if (!System.IO.Directory.Exists(path)) { System.IO.Directory.CreateDirectory(path); } var filesList = Directory.GetFiles(Path.GetDirectoryName(path)); if (filesList.Length != file.Count) { return; } List<FileSort> lstFile = new List<FileSort>(); foreach (var item in filesList) { lstFile.Add(new FileSort() { Name = item, NumBer = Convert.ToInt32(item.Substring(item.IndexOf('^') + 1)) }); } lstFile = lstFile.OrderBy(x => x.NumBer).ToList(); using (var fileStream = new FileStream(baseFileName, FileMode.Create)) { //foreach (var fileSort in filesList) //{ // using (FileStream fileChunk = new FileStream(fileSort, FileMode.Open)) // { // await fileChunk.CopyToAsync(fileStream); // } //} await Policy.Handle<IOException>() .RetryForeverAsync() .ExecuteAsync(async () => { foreach (var fileSort in lstFile) { using (FileStream fileChunk = new FileStream(fileSort.Name, FileMode.Open)) { await fileChunk.CopyToAsync(fileStream); } } }); } //刪除分片檔案 foreach (var dirfile in filesList) { System.IO.File.Delete(dirfile); } } } }

Program配置跨域

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowSpecificOrigin", cors =>
    {
        cors.AllowAnyOrigin();
        cors.AllowAnyHeader();
        //cors.AllowAnyMethod();
    });
});
builder.Services.Configure<FormOptions>(options =>
{
    options.KeyLengthLimit = int.MaxValue;
    options.ValueLengthLimit = int.MaxValue;
    options.MultipartBodyLengthLimit = int.MaxValue;
    options.MultipartHeadersLengthLimit = int.MaxValue;
});
builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.Limits.MaxRequestBodySize = int.MaxValue;
    options.Limits.MaxRequestBufferSize = int.MaxValue;
});
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseCors("AllowSpecificOrigin");
app.UseAuthorization();

app.MapControllers();

app.Run();

前臺VUE

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="https://unpkg.com/[email protected]/dist/vue.global.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <input type="file" id="fileExport" @change="handleFileChange" ref="inputer">
        <button @click="loadfile">開始上傳</button>
        <button @click="load">分片上傳</button>
    </div>
    <script>
        var obj = {
            data() {
                return {
                    Title: "上傳檔案",
                    file: {},
                };
            },
            methods: {
                handleFileChange(e) {
                    let inputDOM = this.$refs.inputer;
                    this.file = inputDOM.files[0];// 通過DOM取檔案資料

                },
                loadfile() {
                    let size = Math.floor(this.file.size / 1024);//計算檔案的大小 
                    let formData = new FormData();//new一個formData事件
                    formData.append("files", this.file); //將file屬性新增到formData裡
                    debugger;
                    fetch("http://localhost:5128/Upload/RuleUploadFile", {
                        method: 'post',
                        body: formData,
                        headers: {
                            /*"Content-Type": "multipart/form-data;",*/
                        }
                    }).then(r => r.json()).then(r => {
                        console.log(r);
                    }).catch(e => {
                        console.log(e);
                    })
                },
                S4() {
                    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
                },

                guid() {
                    return (this.S4() + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + this.S4() + this.S4());
                },
                load() {
                    let size = this.file.size;//檔案大小

                    let maxZrea = 8;              //設定每個分割槽大小 MB
                    let bufferSize = maxZrea * (1024 * 1024);
                    let fileStart = 0;
                    let fileEnd = bufferSize;
                    let arrFile = [];
                    while (fileStart < size) {
                        var fileInfo = {
                            File: this.file.slice(fileStart, fileEnd),
                            Start: fileStart,
                            End: fileEnd
                        }
                        arrFile.push(fileInfo);
                        fileStart = fileEnd;
                        fileEnd = fileStart + bufferSize;
                    }
                    let count = arrFile.length;
                    let filename = this.file.name + "~" + this.guid();
                    for (var i = 0; i < count; i++) {
                        let formData = new FormData();//new一個formData事件
                        formData.append("file", arrFile[i].File); //將file屬性新增到formData裡
                        var url = "http://localhost:5128/Upload/RuleUploadFile?Name=" + filename + "&Number=" + i + "&BufferSize=" + bufferSize + "&Count=" + count + "&Start=" + arrFile[i].Start + "&End=" + arrFile[i].End + "&Size=" + size;
                        /*var url = "http://192.168.0.166:8080/Upload/RuleUploadFile?Name=" + filename + "&Number=" + i + "&BufferSize=" + bufferSize + "&Count=" + count + "&Start=" + arrFile[i].Start + "&End=" + arrFile[i].End + "&Size=" + size;*/
                        //fetch(url, {
                        //    method: 'post',
                        //    body: formData,
                        //    headers: {
                        //        /*"Content-Type": "multipart/form-data;",*/
                        //    }
                        //}).then(r => r.json()).then(r => {
                        //    console.log(r);
                        //}).catch(e => {
                        //    console.log(e);
                        //})
                        axios.post(url, formData, {
                            headers: {
                                "Content-Type": "multipart/form-data;",
                            }
                        }).then(r => {
                            console.log(r);
                        }).catch(e => {
                            console.log(e);
                        });
                    }
                }
            }
        };
        var vm = Vue.createApp(obj).mount("#app");
    </script>
</body>
</html>

實測1G檔案本地開發環境上傳速度在20秒左右,網路環境沒有測試過