隐藏

.NET6 WebApi 大文件上传——分片上传

发布:2022/7/1 0:01:17作者:管理员 来源:本站 浏览次数:799

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/vue@3.2.31/dist/vue.global.js"></script>
    <script src="https://unpkg.com/axios@0.26.1/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秒左右,网络环境没有测试过

源码:https://github.com/1947217768/Upload.git