springboot 大文件切片上传
1. 前端(vue element ui & 原生)
初始变量声明:
currentFile: {}, // 当前上传的文件
bigFileSliceCount: 20, // 大文件切片后的子文件数量(也可使用其它限定方式,如按照文件大小,每10MB切一片,此处采用的是固定切片的子文件数量的方式倒推切片大小)
接口:切片上传图片&合并切片文件
<el-button @click="partUploadClick()" type="info"> 上传文件</el-button>
<input v-show="false" id="uploadPartfile" class="upload-css" type='file' @click.stop=''
@change='handleFileChange($event, currentFile); currentFile = {}' single />
// 文件上传处理
partUploadClick() {
var clickEvent = document.createEvent('MouseEvent'); // 1.创建一个鼠标事件类型
clickEvent.initMouseEvent('click', false, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); // 2.初始化一个click事件
document.getElementById('uploadPartfile').dispatchEvent(clickEvent); // 3.派发(触发)
},
handleFileChange(event, item) {
let inputDom = event.target // input 本身,从这里获取 files<FileList>
let files = inputDom.files // input 中的文件,是 FileList 对象,一个类似数组的文件组,但不是数组,可遍历
var GUID = this.guid();
var file = files[0], //文件对象
name = file.name, //文件名
size = file.size; //总大小
// console.log(size)
if (size <= 524288000) { // 文件大小小于等于500MB,直接整文件上传
this.handleFiles(event, item)
} else { // 大于500MB 切片上传
var shardSize = size / this.bigFileSliceCount, // 根据切片数量,确定每切片的大小是多少
shardCount = Math.ceil(size / shardSize); //总片数
item.download = true
for (var i = 0; i < shardCount; ++i) {
//计算每一片的起始与结束位置
var start = i * shardSize,
end = Math.min(size, start + shardSize);
var partFile = file.slice(start, end);
this.partUpload(GUID, partFile, name, shardCount, i, item.fullPath, item, size);
}
}
},
partUpload: function (GUID, partFile, name, chunks, chunk, filePath, item, size) {
//构造一个表单,FormData是HTML5新增的
const _this = this
var form = new FormData();
form.append("guid", GUID);
form.append("file", partFile); //slice方法用于切出文件的一部分
form.append("fileName", name);
form.append("chunks", chunks); //总片数
form.append("chunk", chunk); //当前是第几片
form.append("filePath", filePath); //文件保存路径
uploadFileByPart(form).then((res) => {
// console.log(res)
let data = res.data
_this.status++;
if (data.code == 200) {
_this.complete = this.status * (100 / this.bigFileSliceCount)
let finishSize = this.status != this.bigFileSliceCount ? (size / this.bigFileSliceCount) * this.status : size
if (finishSize > 1048576) {
_this.finishSize = parseFloat(finishSize / 1048576).toFixed(1) + 'M';
} else if (finishSize > 1024) {
_this.finishSize = parseInt(finishSize / 1024) + 'K';
} else {
_this.finishSize = finishSize + 'B';
}
// console.log(this.status + " / " + chunks)
}
if (this.status == chunks) {
_this.mergeFile(GUID, name, filePath, item);
}
}).catch((err) => {
console.log("请求出错", err);
});
},
// 切片上传文件
export const uploadFileByPart = (fileFormData) => {
return http({
url: `/xxxxx/files/part`,
method: 'post',
data: fileFormData,
async: true,
processData: false,
contentType: false
})
}
mergeFile: function (GUID, name, filePath, item) {
var formMerge = new FormData();
formMerge.append("guid", GUID);
formMerge.append("fileName", name);
formMerge.append("filePath", filePath);
mergeFileByPart(formMerge).then((res) => {
item.download = false
let data = res.data
if (data.code == 200) {
this.$message({
message: '上传成功',
type: 'success'
});
this.getFalcoPath();
}
}).catch((err) => {
item.download = false
console.log("请求出错", err);
});
},
// 切片合并文件
export const mergeFileByPart = (fileFormData) => {
return http({
url: `/xxxxx/files/merge`,
method: 'post',
processData: false,
contentType: false,
data: fileFormData
})
}
guid: function (prefix) {
var counter = 0;
var guid = (+new Date()).toString(32),
i = 0;
for (; i < this.bigFileSliceCount; i++) {
guid += Math.floor(Math.random() * 65535).toString(32);
}
return (prefix || 'wu_') + guid + (counter++).toString(32);
}
// 整文件上传时的处理
handleFiles(event, item) {
let inputDom = event.target // input 本身,从这里获取 files<FileList>
let files = inputDom.files // input 中的文件,是 FileList 对象,一个类似数组的文件组,但不是数组,可遍历
// console.log(files)
let fileFormData = new window.FormData()
fileFormData.append('file', files[0])
item.download = true
// console.log("上传文件路径" + item.fullPath)
const _this = this
uploadFileByPath(fileFormData, item.fullPath, this.gress).then((res) => {
// console.log(res)
item.download = false
inputDom.value = ''
if (res.data.code == 200) {
_this.$message({
message: '上传成功',
type: 'success'
});
this.getFalcoPath();
} else if (res.data.code == 2001) {
this.$message.error('上传失败');
}
}).catch((err) => {
item.download = false
console.log("请求出错", err);
});
},
// 单个文件完整上传文件
export const uploadFileByPath = (fileFormData, filePath, uploadProgress) => {
return http({
url: `/xxxxx/files/upload`,
method: 'post',
onUploadProgress: function (progressEvent) {
uploadProgress(progressEvent)
},
data: fileFormData,
params: { 'filePath': filePath }
})
}
// 上传与下载文件的回调
gress(progress) {
const self = this
this.complete = ((progress.loaded / progress.total) * 100).toFixed(0) if (progress.loaded > 1048576) {
self.finishSize = parseFloat(progress.loaded / 1048576).toFixed(1) + 'M';
} else if (progress.loaded > 1024) {
self.finishSize = parseInt(progress.loaded / 1024) + 'K';
} else {
self.finishSize = progress.loaded + 'B';
}
// console.log("已下载:" + self.finishSize + ' 比例 ' + this.complete)
},
2. 后台
接口:
@ResponseBody
@PostMapping("/xxxxx/files/upload")
public ApiResult upload(@RequestParam(value = "file", required = false) MultipartFile multipartFile, @RequestParam(required = false) String filePath) {
File file=new File(filePath + File.separator + multipartFile.getOriginalFilename());
try {
FileUtil.copy(multipartFile.getBytes(), file);
} catch (IOException e) {
return ApiResult.fail(2001,"上传失败");
}
return ApiResult.ok("上传成功");
}
@PostMapping("/xxxxx/files/part")
@ResponseBody
public ApiResult bigFile(HttpServletRequest request, HttpServletResponse response, String guid, Integer chunk, MultipartFile file, Integer chunks, String filePath, String fileName) {
try {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
if (chunk == null) chunk = 0;
// 临时目录用来存放所有分片文件
String tempFileDir = applicationProperties.getNasPath() + File.separator + "临时文件夹" + File.separator + guid;
File parentFileDir = new File(tempFileDir);
if (!parentFileDir.exists()) {
parentFileDir.mkdirs();
}
// 分片处理时,前台会多次调用上传接口,每次都会上传文件的一部分到后台
log.info(SecurityUtils.getCurrentLogin() + " 上传:" + filePath + File.separator + fileName + "; 切片文件名:" + guid + "_" + chunk + ".part");
File tempPartFile = new File(parentFileDir, guid + "_" + chunk + ".part");
FileUtils.copyInputStreamToFile(file.getInputStream(), tempPartFile);
} } catch (Exception e) {
log.error(SecurityUtils.getCurrentLogin() + " 上传:" + filePath + File.separator + fileName + "; 切片文件名:" + guid + "_" + chunk + ".part" + " error: " + e.getMessage());
e.printStackTrace();
return ApiResult.fail(2009,e.getMessage());
}
return ApiResult.ok(200,"上次成功");
}
@RequestMapping("/xxxxx/files/merge")
@ResponseBody
public ApiResult mergeFile(String guid, String fileName, String filePath) {
try {
log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " start");
String sname = fileName.substring(fileName.lastIndexOf("."));
//时间格式化格式
Date currentTime = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
//获取当前时间并作为时间戳
String timeStamp = simpleDateFormat.format(currentTime);
//拼接新的文件名
String newName = timeStamp + sname;
simpleDateFormat = new SimpleDateFormat("yyyyMM");
String path = applicationProperties.getNasPath() + File.separator + "临时文件夹" + File.separator ;
String tmp = simpleDateFormat.format(currentTime);
File parentFileDir = new File(path + guid);
if (parentFileDir.isDirectory()) {
File destTempFile = new File(filePath, fileName);
if (!destTempFile.exists()) {
//先得到文件的上级目录,并创建上级目录,在创建文件
destTempFile.getParentFile().mkdir();
try {
destTempFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
for (int i = 0; i < parentFileDir.listFiles().length; i++) {
File partFile = new File(parentFileDir, guid + "_" + i + ".part");
FileOutputStream destTempfos = new FileOutputStream(destTempFile, true);
//遍历"所有分片文件"到"最终文件"中
FileUtils.copyFile(partFile, destTempfos);
destTempfos.close();
}
// 删除临时目录中的分片文件
FileUtils.deleteDirectory(parentFileDir);
log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " end ");
return ApiResult.ok(200,"合并成功");
}else{
log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " end error: 没找到目录");
return ApiResult.fail(2007,"没找到目录");
} } catch (Exception e) {
log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " end error: " + e.getMessage());
e.printStackTrace();
return ApiResult.fail(2008,e.getMessage());
} }
工具类:
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils; import java.io.*;
import java.nio.file.*; @Slf4j
public class FileUtil { public static void copy(byte[] in, File out) throws IOException {
Assert.notNull(in, "No input byte array specified");
Assert.notNull(out, "No output File specified");
copy(new ByteArrayInputStream(in), Files.newOutputStream(out.toPath()));
} public static int copy(InputStream in, OutputStream out) throws IOException {
Assert.notNull(in, "No InputStream specified");
Assert.notNull(out, "No OutputStream specified"); try {
return StreamUtils.copy(in, out);
} finally {
try {
in.close();
} catch (IOException ex) {
}
try {
out.close();
} catch (IOException ex) {
}
}
} }
相关引用Class:
import org.apache.commons.io.FileUtils;
springboot 大文件切片上传的更多相关文章
- java springboot 大文件分片上传处理
参考自:https://blog.csdn.net/u014150463/article/details/74044467 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时 ...
- 利用blob对象实现大文件分片上传
首先说分片上传,我们在进行文件上传的时候,因为服务器的限制,会限制每一次上传到服务器的文件大小不会很大,这个时候我们就需要把一个需要上传的文件进行切割,然后分别进行上传到服务器. 假如需要做到这一步, ...
- iOS大文件分片上传和断点续传
总结一下大文件分片上传和断点续传的问题.因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况.http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件 ...
- js实现大文件分片上传的方法
借助js的Blob对象FormData对象可以实现大文件分片上传的功能,关于Blob和FormData的具体使用方法可以到如下地址去查看FormData 对象的使用Blob 对象的使用以下是实现代码, ...
- Node + js实现大文件分片上传基本原理及实践(一)
_ 阅读目录 一:什么是分片上传? 二:理解Blob对象中的slice方法对文件进行分割及其他知识点 三. 使用 spark-md5 生成 md5文件 四. 使用koa+js实现大文件分片上传实践 回 ...
- 视频大文件分片上传(使用webuploader插件)
背景 公司做网盘系统,一直在调用图片服务器的接口上传图片,以前写的,以为简单改一改就可以用 最初要求 php 上传多种视频格式,支持大文件,并可以封面截图,时长统计 问题 1.上传到阿里云服务器,13 ...
- vue大文件分片上传插件
最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...
- nodeJs + js 大文件分片上传
简单的文件上传 一.准备文件上传的条件: 1.安装nodejs环境 2.安装vue环境 3.验证环境是否安装成功 二.实现上传步骤 1.前端部分使用 vue-cli 脚手架,搭建一个 demo 版本, ...
- PHP实现大文件的上传设置
打开php.ini,首先找到 ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; 区域,有影响文件上传的以下几个参数: file_uploads = ...
- Webuploader 大文件分片上传
百度Webuploader 大文件分片上传(.net接收) 前阵子要做个大文件上传的功能,找来找去发现Webuploader还不错,关于她的介绍我就不再赘述. 动手前,在园子里找到了一篇不错的分片 ...
随机推荐
- 尝试官方的第一个SpringNative 0.11程序(WSL2)
Spring Native是Spring推出微服务体系Spring Cloud之后的又一大举动,从名字可以猜出,Spring Native是一门面向云原生的技术.如果你还对这个概念不太理解,可以多看一 ...
- 松灵机器人scout mini小车 自主导航(1)
松灵机器人scout mini小车 自主导航 最近实验室来了个松灵机器人scout mini的底座.本着学习的目的对其进行了研究,从最基础的连接开始. 1.硬件连接准备 松灵scout mini提供了 ...
- 网易数帆实时数据湖 Arctic 的探索和实践
作者 | 蔡芳芳 采访嘉宾 | 马进 网易数帆平台开发专家 数据中台也要从离线为主走向实时化,湖仓一体是第一步. 数据从离线到实时是当前一个很大的趋势,但要建设实时数据.应用实时数据还面临两个难题.首 ...
- [oeasy]python0119_语言的演化_拉丁字符_罗马帝国_罗马体
罗马拉丁字符 回忆上次内容 起源于埃及的 圣书体 象形文字 在与 两河流域的 苏美尔楔形文字 结合后 经过 腓尼基人 的拼音化 和 广泛传播 终于来到了 希腊 添加图片注释,不超过 140 ...
- npm私服 verdaccio 搭建
1.什么是npm 私服 我们前端(web,nodejs)平常使用的各种包,什么vue,react,react-router, zustand等,都会从 https://registry.npmjs.o ...
- MFC BCG 一些记录
MFC: UpdateData (TRUE) // 更新值到控件 UpdateData (TRUE) // 更新控件到值DECLAREMESSAGEMAPBEGINMESSAGEMAP(d ...
- WordPress基础之基本SEO设置
基础内容,不会涉及过深,在谷歌SEO教程中会做详细的介绍,我这里只简单讲下. 1. SEO介绍 SEO,又名搜索引擎优化(Search Engine Optimization,缩写为SEO)是透过了解 ...
- 【Hibernate】Re02 官网介绍
介绍: 创始人:Gavin King.EJB3.0专家,JBoss核心成员之一,<Hibernate In Action>作者 Hibernate是ORM的解决方案. 优点: 1.功能强大 ...
- 实用英语:英文中经常出现“ i.e.、e.g.、etc."缩写究竟啥意思?
地址: https://mp.weixin.qq.com/s?__biz=MzIyMzU5NDE1Mw==&mid=2247487840&idx=1&sn=013992683c ...
- 不务正业的再次胡想——chatgpt在“智能辅助编程”外的另一个可能场景"智能论文写作辅助”
在chatgpt4出来后震惊了很多人,但是很多人也觉得好像用处不大:可以说chatgpt4确实更加智能了,在语言对话上更加的智能,很多情况下已经很难分辨出这货是个机器人,但是现在这东西好像确实也没有太 ...