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. 移动web布局方法

    继续更新移动端的一个布局,这也是经典中的经典,当初只知道个rem和vwvh适配,其实这里面还有很多的门道不只是一个适配这么简单 一.前置 1.背景缩放 我们都知道做移动端,给的图都是二倍图,你拿来用直 ...

  2. 谈谈你对MVVM开发模式和MVT的理解?

    MVVM分为Model.View.ViewModel三者. Model 代表数据模型,数据和业务逻辑都在Model层中定义: View 代表UI视图,负责数据的展示: ViewModel 负责监听 M ...

  3. 解决方案 | winrar 使用命令行解压到同名文件夹 (QTTabBar 中创建一个【解压文件】命令按钮的设置)

    需求:我们经常需要把rar或者zip解压到当前文件夹,如果是直接解压的话可能会解压出来很多文件,事实上我们当然可以通过右键解压到这个指定文件夹. 但是 经过查询知道,如果是指定文件夹好说,直接指定.\ ...

  4. 转载 | ofd转pdf最好用的软件,ofd文件如何转化成pdf?

    1.背景 需要将ofd转换为pdf 2.使用方法 使用taurusxin 开发的软件Ofd2Pdf.exe即可实现,软件版权归原作者所有.这里表示感谢! 3.下载地址 官网:https://githu ...

  5. [oeasy]python0127_中文系统_gbk_BIG5_南极星_内码转化

    中文系统bgk 回忆上次内容 汉字字形通过 点阵式打字机 像素级寻址的屏幕 进入了计算机的世界   ​   添加图片注释,不超过 140 字(可选)   在海峡对岸的台湾同胞 也进入了汉字时代   他 ...

  6. Day 3 - 单调栈、单调队列、凸包与斜率优化

    单调栈 引入 何为单调栈?顾名思义,单调栈即满足单调性的栈结构.与单调队列相比,其只在一端进行进出. 为了描述方便,以下举例及伪代码以维护一个整数的单调递增栈为例. 过程 插入 将一个元素插入单调栈时 ...

  7. ASP.NET Core 3.x 三种【输入验证】方式

    验证要做三件事 定义验证规则 按验证规则进行检查 报告验证的错误. 在把错误报告给API消费者的时候,报告里并不包含到底是服务端还是API消费者引起的错误,这是状态码的工作.而通常响应的Body里面会 ...

  8. sed 进阶使用

    sed 进阶使用 工作原理 sed 维护两个数据缓冲区: 活动模式空间 和 辅助保持空间 两者最初都是空的 sed 通过对每一行输入执行以下循环进行操作 从输入流中读取一行,删除任何尾随的换行符,并将 ...

  9. 20+前端常用的vscode插件(总结推荐)

    本篇文章给大家总结分享20多个前端常用的vscode插件.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. 1. vscode 简介vscode是微软开发的的一款代码编辑器,就如官网上 ...

  10. WordPress基础之基本SEO设置

    基础内容,不会涉及过深,在谷歌SEO教程中会做详细的介绍,我这里只简单讲下. 1. SEO介绍 SEO,又名搜索引擎优化(Search Engine Optimization,缩写为SEO)是透过了解 ...