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. 搭建hadoop集群关键步骤--以三节点为例

    搭建三节点的hadoop集群: 要求: 主机名称 备注 IP地址 功能 hadoop01 Master 主节点 192.168.211.134 NameNode.DataNode.ResourceMa ...

  2. 继续我们的复习之路——webapi

    前面断更几天是因为在住安心复习DOM BOM的内容 不得不说 还得是DOM 在这一章节的复习内容中  涌现出了很多又代表意义 经典的一些小案例 而且 还是有些难度的 有一两个我反正是自己独立完成不了 ...

  3. python 自动化神器 多平台纯代码RPA办公自动化python框架

    ​ Pyaibote是一款专注于纯代码RPA(机器人流程自动化)的强大工具,支持Android.Browser和Windows三大主流平台.无论您需要自动化安卓应用.浏览器操作还是Windows应用程 ...

  4. SpringBoot+Mybatis整合出现org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)的解决

    在搭建自己的后台管理,遇到一个比较小问题,顺便记录了一下. 启动SpringBoot后台时,前端访问后台执行Mybatis时,出现了这样的报错: org.apache.ibatis.binding.B ...

  5. Python爬取小说+Servlet+C3P0+MVC构建小说api

    一.摘要: 使用python爬取网络小说数据存入数据库,利用C3P0数据库连接池获取数据库数据,采用MVC三层架构对数据库数据进行操作,输出JSON格式数据到前端页面 二.内容: 1.gitee外链消 ...

  6. 做独立开发者,能在 AppStore 赚到多少钱?

    成为一名独立开发者,不用朝九晚五的上班,开发自己感兴趣的产品,在AppStore里赚美金,这可能是很多程序员的梦想,今天就来盘一盘,这个梦想实现的概率有多少. (Solo社区 投稿) 先来了解一些数据 ...

  7. K8S 中的 CRI、OCI、CRI shim、containerd

    哈喽大家好,我是咸鱼. 好久没发文了,最近这段时间都在学 K8S.不知道大家是不是和咸鱼一样,刚开始学 K8S.Docker 的时候,往往被 CRI.OCI.CRI shim.containerd 这 ...

  8. 可视化—gojs 超多超实用经验分享(二)

    想了想序号还是接上一篇分享的的序号接着写,如果在本文中没有获取需要的答案,可以移步去看看上一篇的分享.gojs 超多超实用经验分享(一) 目录 22. 指定线段连接到节点的某一个特定的接口上 23. ...

  9. P10507 Georgia and Bob 题解

    思路 对棋子坐标排序,\(x_{i}-x_{i-1}-1\) 就是棋子可以移动的距离. 移动第 \(i\) 个棋子,相当于将 \(i+1\) 的移动范围扩大. 这于是变形成了一个台阶 nim 博弈论. ...

  10. 【CentOS】tar包安装Tomcat

    下载Linux版本的Tomcat[Tar包] 上传到Linux 解压Tar包 tar -zxvf apache-tomcat-8.5.55.tar.gz 目录重命名简化名称[可不做] mv apach ...