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 大文件切片上传的更多相关文章

  1. java springboot 大文件分片上传处理

    参考自:https://blog.csdn.net/u014150463/article/details/74044467 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时 ...

  2. 利用blob对象实现大文件分片上传

    首先说分片上传,我们在进行文件上传的时候,因为服务器的限制,会限制每一次上传到服务器的文件大小不会很大,这个时候我们就需要把一个需要上传的文件进行切割,然后分别进行上传到服务器. 假如需要做到这一步, ...

  3. iOS大文件分片上传和断点续传

    总结一下大文件分片上传和断点续传的问题.因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况.http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件 ...

  4. js实现大文件分片上传的方法

    借助js的Blob对象FormData对象可以实现大文件分片上传的功能,关于Blob和FormData的具体使用方法可以到如下地址去查看FormData 对象的使用Blob 对象的使用以下是实现代码, ...

  5. Node + js实现大文件分片上传基本原理及实践(一)

    _ 阅读目录 一:什么是分片上传? 二:理解Blob对象中的slice方法对文件进行分割及其他知识点 三. 使用 spark-md5 生成 md5文件 四. 使用koa+js实现大文件分片上传实践 回 ...

  6. 视频大文件分片上传(使用webuploader插件)

    背景 公司做网盘系统,一直在调用图片服务器的接口上传图片,以前写的,以为简单改一改就可以用 最初要求 php 上传多种视频格式,支持大文件,并可以封面截图,时长统计 问题 1.上传到阿里云服务器,13 ...

  7. vue大文件分片上传插件

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

  8. nodeJs + js 大文件分片上传

    简单的文件上传 一.准备文件上传的条件: 1.安装nodejs环境 2.安装vue环境 3.验证环境是否安装成功 二.实现上传步骤 1.前端部分使用 vue-cli 脚手架,搭建一个 demo 版本, ...

  9. PHP实现大文件的上传设置

    打开php.ini,首先找到 ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; 区域,有影响文件上传的以下几个参数: file_uploads = ...

  10. Webuploader 大文件分片上传

    百度Webuploader 大文件分片上传(.net接收)   前阵子要做个大文件上传的功能,找来找去发现Webuploader还不错,关于她的介绍我就不再赘述. 动手前,在园子里找到了一篇不错的分片 ...

随机推荐

  1. 2.上传hdfs系统:将logs目录下的日志文件每隔十分钟上传一次 要求:上传后的文件名修为:2017111513xx.log_copy

    先在hdfs系统创建文件夹logshadoop fs -mkdir /logs 编辑shell脚本 filemv.sh #!/bin/bashPATH=/usr/local/bin:/bin:/usr ...

  2. GaussDB(DWS)性能调优,解决DM区大内存占用问题

    本文分享自华为云社区<GaussDB(DWS)性能调优:DM区优化案例--维度表关联条件存在会计期>,作者: O泡果奶~. 当前DM(P1.P3.CBGDM)存在维度表与主表关联时使用会计 ...

  3. python3 安装pymssql失败 pip3 install pymssql

    python3 安装pymssql失败 报错信息: AttributeError: module 'platform' has no attribute 'linux_distribution' 解决 ...

  4. Mac下Eclipse打不开了怎么办

    其实这个问题能搜到很多答案,但是对我有效的只有下面这一种. 背景 我需要使用到Eclipse,就下载了它的特定版本,用于开发RAP的. 连续两次都是使用后关闭,就再也打不开了. 选定的解决方案 试了好 ...

  5. MFC基于对话框工程笔记->新建MFC对话框

    一.前言 最近用MFC做了一个对话框小工具,学到了很多知识,现在做一下总结,以作备忘.(如有不足,后期添加修改) 二.MFC使用->新建MFC对话框 操作环境:VS2010 主要使用语言:C.C ...

  6. 树莓派4B-高精度驱动步进电机

    树莓派4B-高精度驱动步进电机 项目介绍 利用4B树莓派控制步进电机转动,精度可达:0.0144度 (即360度/25000) 适用于非常精密的角度转动. 舵机的精度为1度,无法实现超高精度控制. 硬 ...

  7. Java 反射获取对象里的值

    最近在负责邮件服务,里面会涉及到很多Email模板,这里我使用到了java的模板引擎:jetbrick-template,需要使用Map集合一个个往里面设置值,然后调用模板方法,进行替换.实体类一个个 ...

  8. Java-Request对象是用来回去请求信息,得到页面的请求

    1.Request 1.1 request对象和response对象的原理(了解) request对象和response对象是由服务器创建的,我们来使用他们即可 request对象是用来回去请求信息, ...

  9. 疑难杂症(已解决) | 为什么出现python中tkinter创建界面需要一闪而过才进入主窗口?

    一.具体问题 如图所示,我编写了一个主窗口的程序(如下所示,有兴趣的可以自己复制后运行),发现需要先进入第一个窗口再进入主界面,这不符合逻辑. 代码区域(完整代码): from tkinter imp ...

  10. ps -ef | grep xxx 解释

    上述内容为:命令拆解: ps:将某个进程显示出来-A 显示所有程序.-e 此参数的效果和指定"A"参数相同.-f 显示UID,PPIP,C与STIME栏位.grep命令是查找中间的 ...