Net Core 3.1大文件分片上传
在很多实际的开发场景中,我们都需要上传文件,比如图片,文档,pdf等等。而除了图片相对比较小以外(现在很多智能手机拍出的图片,都是几M的大小),其它文档相对来说都比较大。而我们在上传文件的时候,一般服务器都是对上传文件有大小限制的,就算我们把服务器的上传文件大小改到1G,我们在上传的时候,也难免出现接口超时,界面卡死等现象。所以这里我用net core 3.0 + vue-antd-pro...
·
在很多实际的开发场景中,我们都需要上传文件,比如图片,文档,pdf等等。而除了图片相对比较小以外(现在很多智能手机拍出的图片,都是几M的大小),其它文档相对来说都比较大。
而我们在上传文件的时候,一般服务器都是对上传文件有大小限制的,就算我们把服务器的上传文件大小改到1G,我们在上传的时候,也难免出现接口超时,界面卡死等现象。
所以这里我用net core 3.0 + vue-antd-pro的上传插件,实现了一个大文件分片上传的功能,比如100M的文件,我们把它分割成50个文件,每个文件2M(分割文件的大小,可以自定义)。这样我们上传的时候就不会经常出现超时,界面卡死的问题。
先看看效果图:
net core 3.0大文件分片上传的代码如下:
using JuCheap.Core.Infrastructure.Extentions;
using JuCheap.Core.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace JuCheap.Core.WebApi.Controllers
{
/// <summary>
/// 文件上传api
/// </summary>
[Route("api/file")]
public class FileUploadController : BaseApiController
{
private readonly ILogger<FileUploadController> _logger;
private readonly IWebHostEnvironment _webHostEnvironment;
/// <summary>
/// 文件存放的根路径
/// </summary>
private readonly string _baseUploadDir;
/// <summary>
/// ctor
/// </summary>
public FileUploadController(ILogger<FileUploadController> logger,
IWebHostEnvironment hostEnvironment)
{
_logger = logger;
_webHostEnvironment = hostEnvironment;
_baseUploadDir = string.Concat(_webHostEnvironment.ContentRootPath, "\\uploads\\");
}
/// <summary>
/// 分片上传文件
/// </summary>
[HttpPost]
[Route("upload")]
public async Task<ActionResult<FileResponseDto>> Upload()
{
var fileName = Request.Form["name"];
//当前分块序号
var index = Request.Form["chunk"].ToString().ToInt();
//所有块数
var maxChunk = Request.Form["maxChunk"].ToString().ToInt();
//前端传来的GUID号
var guid = Request.Form["guid"];
//临时保存分块的目录
var dir = Path.Combine(_baseUploadDir, guid);
dir.CreateDirectoryIfNotExists();
//分块文件名为索引名,更严谨一些可以加上是否存在的判断,防止多线程时并发冲突
var filePath = Path.Combine(dir, index.ToString());
//表单中取得分块文件
var file = Request.Form.Files["file"];
//获取文件扩展名
//var extension = file.FileName.Substring(file.FileName.LastIndexOf(".") + 1, (file.FileName.Length - file.FileName.LastIndexOf(".") - 1));
var filePathWithFileName = string.Concat(filePath, fileName);
using (var stream = new FileStream(filePathWithFileName, FileMode.Create))
{
await file.CopyToAsync(stream);
}
//如果是最后一个分块, 则合并文件
var fileResponseDto = new FileResponseDto();
if (index == maxChunk - 1)
{
await MergeFileAsync(fileName, guid);
fileResponseDto.Completed = true;
}
return Ok(fileResponseDto);
}
/// <summary>
/// 合并分片的文件
/// </summary>
/// <param name="fileName">文件名称</param>
/// <param name="guid">文件guid</param>
private async Task MergeFileAsync(string fileName, string guid)
{
//临时文件夹
var dir = Path.Combine(_baseUploadDir, guid);
//获得下面的所有文件
var files = Directory.GetFiles(dir);
var yearMonth = DateTime.Now.ToString("yyyyMM");
//最终的文件名(demo中保存的是它上传时候的文件名,实际操作肯定不能这样)
var finalDir = Path.Combine(_baseUploadDir, yearMonth, guid);
finalDir.CreateDirectoryIfNotExists();
var finalPath = Path.Combine(finalDir, fileName);
using (var fs = new FileStream(finalPath, FileMode.Create))
{
//排一下序,保证从0-N Write
var fileParts = files.OrderBy(x => x.Length).ThenBy(x => x);
foreach (var part in fileParts)
{
var bytes = await System.IO.File.ReadAllBytesAsync(part);
await fs.WriteAsync(bytes, 0, bytes.Length);
bytes = null;
//删除分块
System.IO.File.Delete(part);
}
await fs.FlushAsync();
fs.Close();
//删除临时文件夹和分片文件
Directory.Delete(dir);
}
}
}
}
前台页面js代码如下:
<template>
<div>
<a-card :loading="loading" title="说明">
<p>1.支持自定义上传方式</p>
<p>2.支持大文件上传, 把一个大文件,分割成若干个小文件,上传到服务</p>
<p>3.支持显示上传进度</p>
</a-card>
<p></p>
<a-upload-dragger
name="file"
:multiple="true"
action="/api/file/upload"
@change="handleChange"
:customRequest="customRequest"
>
<p class="ant-upload-drag-icon">
<a-icon type="inbox" />
</p>
<p class="ant-upload-text">点击或者拖拽文件到此区域,上传文件</p>
<p class="ant-upload-hint">
支持单个文件或批量上传文件。严禁上传公司机密资料
</p>
</a-upload-dragger>
</div>
</template>
<script>
import defaultSettings from '@/config/defaultSettings'
import ProcessHelper from '@/utils/helper/ProcessHelper'
const rootUrl = () => {
if (ProcessHelper.isProduction() || ProcessHelper.isPreview()) {
return defaultSettings.publishRootUrl
} else {
return defaultSettings.localRootUrl
}
}
export default {
data() {
return {
//已上传的文件
fileList:[],
loading: true
}
},
mounted(){
this.loading = false
},
methods:{
handleChange(info) {
let me = this
const status = info.file.status
if (status !== 'uploading') {//removed
console.log(info.file, info.fileList);
}
if (status === 'removed') {
me.fileList.forEach(function(item, index){
if(item.uid === info.file.uid){
me.fileList.splice(index, 1)
}
})
}
if (status === 'done') {
} else if (status === 'error') {
}
},
customRequest(option){
console.log(option)
let url = rootUrl() + option.action
let createGuid = function() {
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1)
}
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4())
}
this.fileUpload(url, option, 0, createGuid())
},
//上传文件
fileUpload(uploadUrl, option, chunk, guid) {
let file = option.file
//每次上传文件的大小
let chunkSize = 1024 * 1024
//最大上传大小 默认100M
let maxSize = 1024 * 1024 * 100
let maxChunk = Math.ceil((file.size / chunkSize))
let formData = new FormData()
//将文件进行分段
let fileSize = file.size
if (fileSize > maxSize) {
this.$message.error("文件大小不能超过" + (maxSize / 1024 / 1024) + "M")
return
}
//当前上传进度
let currentPercent = parseInt((chunk / maxChunk) * 100)
option.onProgress({ percent: currentPercent })
formData.append('file', file.slice(chunk * chunkSize, (chunk + 1) * chunkSize))
formData.append('name', file.name)
formData.append('chunk', chunk)
formData.append('maxChunk', maxChunk)
formData.append('guid', guid)
this.$http.post(uploadUrl, formData).then(resJson => {
this.confirmLoading = false
if (resJson.success) {
if(!resJson.data.completed){
this.fileUpload(uploadUrl, option, ++chunk, guid)
}else{
this.$message.success("文件上传成功")
option.onProgress({ percent: 100 })
option.onSuccess()
}
} else {
this.$message.error(resJson.message)
}
})
}
}
}
</script>
效果查看地址:http://core.jucheap.com
账号:jucheap 密码:qwaszx
更多推荐
已为社区贡献1条内容
所有评论(0)