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还不错,关于她的介绍我就不再赘述. 动手前,在园子里找到了一篇不错的分片 ...
随机推荐
- debian11安装备忘
1. 网卡驱动 参考网址:如何安装Debian RTL8821CE驱动? 2. 分辨率 貌似还是有点问题,还要进一步研究一下 参考网址:虚拟机中debian11修改控制台(console)分辨率|li ...
- 解决方案 | onenote无法同步,显示:证书错误,应用程序在加载SSL库是遇到内部错误。
解决方案:一般是公司网络或者学校网络的问题,更换手机使用的数据流量热点无线网络即可.
- 【原创软件】第2期:CAD文字快速批量替换工具CFR(CAD_FastReplace_V4)
01 背景 由于工作需要,开发了一套CAD文字快速批量替换软件CFR.主要目的是:实现dwg文件一次性完成单对/多对词组快速批量替换. 02 主要功能特色 (1)无需打开CAD,快速实现文字批量替换. ...
- 2024秋招西山居游戏开发SEED种子实习笔试题
西山居游戏开发SEED种子实习 2024年秋招笔试题目,仅供参考,请大佬多多指教 选择题 逆波兰数,TCP,操作系统FIFO,C语言大小端 填空题 一道LUA脚本写结果,一道并发存储优化题,计算机系统 ...
- webgl未使用独立显卡报告2
楔子 在上一篇文章 <# [https://juejin.cn/post/7074771064286347301] webgl未使用独立显卡报告> 发表后,有读者在公众号给我发了一段评论, ...
- 深入解读RabbitMQ工作原理
RabbitMQ简介 在介绍RabbitMQ之前首先要介绍一下MQ,MQ是什么?MQ全称是Message Queue,可以理解为消息队列的意思. RabbitMQ是一个实现了AMQP(Advanced ...
- Memcache 与 Memcached 的区别
Memcached 从0.2.0开始,要求PHP版本>=5.2.0,Memcache 要求PHP版本>=4.3. Memcached 最后发布时间为2018-12-24,Memcache ...
- [oeasy]python0133_[趣味拓展]好玩的unicode字符_另类字符_上下颠倒英文字符
另类字符 回忆上次内容 上次再次输出了大红心<span style="font-size:64px;color:red"></span> 找到了红心对应的编 ...
- [oeasy]python0051_ 转义_escape_字符_character_单引号_双引号_反引号_ 退格键
转义字符 回忆上次内容 上次研究的是进制转化 10进制可以转化为其他形式 bin oct hex 其他进制也可以转化为10进制 int 可以设置base来决定转为多少进制 回忆一下 我们为什么会有八进 ...
- ABC357-C题解
最近一直掉分,谔谔. 分析 发现机房里面除了我以外都用递归写的,那我就来讲一种非递归的吧. 考虑第 \(i\) 级地毯拆成九块以后其实就是八块第 \(i-1\) 级地毯与一块大小为 \(3^{i-1} ...