项目框架 net6 webapi
在 Program.cs 文件中添加如下代码
不然会出现下面的限制错误
From表单限制:Failed to read the request form. Multipart body length limit 134217728 exceeded
请求体超长:Request body too large. The max request body size is 30000000 bytes.
builder.Services
.Configure(x =>
{
x.AllowSynchronousIO = true; // 配置可以同步请求读取流数据
x.Limits.MaxRequestBodySize = int.MaxValue;
})
.Configure(x =>
{
x.AllowSynchronousIO = true;
x.MaxRequestBodySize = int.MaxValue; // 设置请求体可接收的最大值
})
.Configure(x =>
{
// 设置表单上传文件的大小限制
// 如果不配置,默认是128兆
x.MultipartBodyLengthLimit = int.MaxValue;
});
找到对应程序的 web.config
添加如下代码配置:
<!-- 1000 MB in bytes -->
"1048576000" />
</requestFiltering>
</security>
若缺少 system.webServer 等节点,添加上即可
在 conf 文件里的 nginx.conf 配置文件 http 中添加节点
client_max_body_size 1000m;
///
/// 功 能: N/A
/// V0.01 2023/10/24 17:56:36 xliu 初版
/// </summary>
public class UploadFileInChunksVO
{
///
/// 分片后的文件
/// </summary>
public IFormFile File { get; set; }
///
/// 当前块,从1开始
/// </summary>
public int ChunkNumber { get; set; }
///
/// 总块数
/// </summary>
public int TotalChunks { get; set; }
}
必须添加 [FromForm] 标识,不然 FIle 识别不到
AppSettings 是一个自行实现读取配置文件的方法
RunInterceptException 是自定义的异常类,统一错误捕获处会对这个做 400 的异常处理
public async Task UploadFile([FromForm] UploadFileInChunksVO chunksVO)
{
if (chunksVO.ChunkNumber == 0 || chunksVO.TotalChunks == 0)
throw new RunInterceptException("上传的数据块标识能为0");
// 创建用于存储上传文件的文件夹
// 可以是读取当前服务的地址,我这边项目是集群化的所有存储地址必须是一个地方不然没办法合并
var path = AppSettings.app(new string[] { "Startup", "AppData" });
if (path == null || path.IsNullOrEmpty())
throw new RunInterceptException("文件存储服务路径为空");
var folderPath = Path.Combine(path, "Uploads", "JD_EDI");
var tempPath = Path.Combine(folderPath, "Temp");
await _fileService.UploadFileInChunksAsync(chunksVO.File, tempPath, chunksVO.ChunkNumber);
// 上传最后一块了 进行合并
if (chunksVO.ChunkNumber == chunksVO.TotalChunks)
{
// 构造合并后的文件路径
var mergedFilePath = Path.Combine(folderPath, chunksVO.File.FileName);
await _fileService.MergeFileAsync(mergedFilePath, tempPath, chunksVO.File.FileName, chunksVO.TotalChunks);
// 合并后的操作
var res = await _ediService.SalesStockAsync(mergedFilePath);
return Ok("处理成功数:" + res);
}
return Ok("接收成功");
}
项目做了接口、服务分离。使用依赖注入的方式
若没这项要求的 可以直接使用后面的方法实现
///
/// 功 能: 上传文件服务
/// V0.01 2023/10/24 15:01:01 xliu 初版
/// </summary>
public interface IUploadFileService
{
///
/// 分片上传文件
/// </summary>
/// <param name="file">正在上传的文件</param>
/// <param name="tempFilePath">临时存储分片数据的目录</param>
/// <param name="chunkNumber">当前分片块</param>
/// 最终文件保存路径</returns>
Task UploadFileInChunksAsync(IFormFile file, string tempFilePath , int chunkNumber);
///
/// 用于合并文件块并处理完整文件的方法
/// </summary>
/// <param name="mergedFilePath">合并后文件的保存地址</param>
/// <param name="tempPath">分片文件的保存地址</param>
/// <param name="fileName"></param>
/// <param name="totalChunks"></param>
/// </returns>
Task MergeFileAsync(string mergedFilePath, string tempPath, string fileName, int totalChunks);
}
///
/// 功 能: N/A
/// V0.01 2023/10/24 15:05:09 xliu 初版
/// </summary>
public class UploadFileService : IUploadFileService
{
public async Task UploadFileInChunksAsync(IFormFile file, string tempPath, int chunkNumber)
{
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
// 构造当前块文件的路径
var filePath = Path.Combine(tempPath, file.FileName + "_" + chunkNumber);
// 将文件块写入磁盘
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
return filePath;
}
public async Task MergeFileAsync(string mergedFilePath, string tempPath, string fileName, int totalChunks)
{
// 创建用于存储合并后文件的流
using var mergedFileStream = new FileStream(mergedFilePath, FileMode.Create);
// 循环处理每个文件块
for (int i = 1; i <= totalChunks; i++)
{
// 构造当前文件块的路径
var chunkFilePath = Path.Combine(tempPath, fileName + "_" + i);
// 创建用于读取文件块的流
using (var chunkFileStream = new FileStream(chunkFilePath, FileMode.Open))
{
// 将文件块内容复制到合并文件流中
await chunkFileStream.CopyToAsync(mergedFileStream);
}
// 删除已合并的文件块
System.IO.File.Delete(chunkFilePath);
}
}
这边只给到 postman 的示例
前端实现 无非就是根据文件大小切分成多个文件 单次上传一部分
每次上传变化 file 和 chuckNumber 即可,当 chunkNumber 和 totalChunks 相等时便上传完成