springboot + vue + vue-simple-uploader 实现大文件分片上传
原文章https://www.cnblogs.com/hz-handofgod/p/11985170.html 适当修改参考官方组件文档https://github.com/simple-uploader/vue-uploader前台VUE添加npm包npm install vue-simple-uploader --savemain.jsimport upLoader from 'vue-sim
原文章 https://www.cnblogs.com/hz-handofgod/p/11985170.html 适当修改
参考官方组件文档 https://github.com/simple-uploader/vue-uploader
目录
前台VUE
添加npm包
npm install vue-simple-uploader --save
main.js
import upLoader from 'vue-simple-uploader'
Vue.use(upLoader)
vue页面
<template>
<div>
<div>
<uploader
:options="options"
:file-status-text="statusText"
class="uploader-example"
ref="uploader"
@file-success="fileSuccess"
@file-error="fileError"
style="width: 100%;"
>
<uploader-unsupport></uploader-unsupport>
<uploader-drop>
<p>上传界面</p>
<uploader-btn single>选择文件</uploader-btn>
</uploader-drop>
<uploader-list></uploader-list>
</uploader>
</div>
</div>
</template>
<script>
export default {
name: 'UploadTest',
data() {
return {
recording: {},
options: {
// https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
target: '/apis/upload/uploadChunk', //后台上传文件地址
testChunks: false, //不校验
chunkSize: 2048000 //每片大小
},
statusText: {
success: '上传成功',
error: '上传失败',
uploading: '上传中',
paused: '暂停中',
waiting: '等待中'
}
}
},
methods: {
//上传成功的回调事件
fileSuccess(rootFile, file, message, chunk) {
var name = message.split('\\')
var nameLast = name[name.length - 1]
// 文件链接
this.recording.recordingUrl = 'http://localhost:8090/static/' + nameLast
// 打印访问服务器的文件地址
alert(this.recording.recordingUrl)
},
fileError() {
}
},
mounted() {
// 获取uploader对象
this.$nextTick(() => {
window.uploader = this.$refs.uploader.uploader
})
}
}
</script>
<style scoped>
.uploader-example {
width: 100%;
padding: 15px;
margin: 50px auto 0;
font-size: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
}
.uploader-example .uploader-btn {
margin-right: 4px;
}
.uploader-example .uploader-list {
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
</style>
关于上传选择文件的单选多选,uploader-btn标签的single属性控制
页面展示效果
后台
pom添加两个依赖
<!--文件上传-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
util文件 Uploader
package com.cei.xyd_association.util;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Iterator;
/**
* 断点续传
*
* @author
*/
public class Uploader {
/**
* 临时文件夹
*/
private String temporaryFolder;
/**
* 最大文件大小 2GB
*/
private Integer maxFileSize = 2048000000;
// private String fileParameterName;
public Uploader(String temporaryFolder, String fileParameterName) {
this.temporaryFolder = temporaryFolder;
File file = new File(temporaryFolder);
if (!file.exists()) {
file.mkdirs();
}
// if (fileParameterName == null) {
// this.fileParameterName = "file";
// } else {
// this.fileParameterName = fileParameterName;
// }
}
public String cleanIdentifier(String identifier) {
return identifier.replaceAll("[^0-9A-Za-z_-]", "");
}
public String getChunkFilename(int chunkNumber, String identifier) {
identifier = cleanIdentifier(identifier);
return new File(temporaryFolder, "jwj-" + identifier + '-' + chunkNumber).getAbsolutePath();
}
public String validateRequest(int chunkNumber, int chunkSize, int totalSize, String identifier, String filename,
Integer fileSize) {
identifier = cleanIdentifier(identifier);
if (chunkNumber == 0 || chunkSize == 0 || totalSize == 0 || identifier.length() == 0
|| filename.length() == 0) {
return "non_uploader_request";
}
int numberOfChunks = (int) Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);
if (chunkNumber > numberOfChunks) {
return "invalid_uploader_request1";
}
if (this.maxFileSize != null && totalSize > this.maxFileSize) {
return "invalid_uploader_request2";
}
if (fileSize != null) {
if (chunkNumber < numberOfChunks && fileSize != chunkSize) {
return "invalid_uploader_request3";
}
if (numberOfChunks > 1 && chunkNumber == numberOfChunks
&& fileSize != ((totalSize % chunkSize) + chunkSize)) {
return "invalid_uploader_request4";
}
if (numberOfChunks == 1 && fileSize != totalSize) {
return "invalid_uploader_request5";
}
}
return "valid";
}
public int getParamInt(HttpServletRequest req, String key, int def) {
String value = req.getParameter(key);
try {
return Integer.parseInt(value);
} catch (Exception e) {
}
return def;
}
public String getParamString(HttpServletRequest req, String key, String def) {
String value = req.getParameter(key);
try {
return value == null ? def : value;
} catch (Exception e) {
}
return def;
}
public void get(HttpServletRequest req, UploadListener listener) {
int chunkNumber = this.getParamInt(req, "chunkNumber", 0);
int chunkSize = this.getParamInt(req, "chunkSize", 0);
int totalSize = this.getParamInt(req, "totalSize", 0);
String identifier = this.getParamString(req, "identifier", "");
String filename = this.getParamString(req, "filename", "");
if (validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename, null).equals("valid")) {
String chunkFilename = getChunkFilename(chunkNumber, identifier);
if (new File(chunkFilename).exists()) {
listener.callback("found", chunkFilename, filename, identifier, null);
} else {
listener.callback("not_found", null, null, null, null);
}
} else {
listener.callback("not_found", null, null, null, null);
}
}
public void post(HttpServletRequest req, UploadListener listener) throws IllegalStateException, IOException {
int chunkNumber = this.getParamInt(req, "chunkNumber", 0);
int chunkSize = this.getParamInt(req, "chunkSize", 0);
int totalSize = this.getParamInt(req, "totalSize", 0);
String identifier = this.getParamString(req, "identifier", "");
String filename = this.getParamString(req, "filename", "");
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(req.getSession().getServletContext());
if (multipartResolver.isMultipart(req)) {
// 将request变成多部分request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) req;
// 获取multiRequest 中所有的文件名
Iterator<String> iter = multiRequest.getFileNames();
while (iter.hasNext()) {
String name = iter.next().toString();
// if (!this.fileParameterName.equals(name)) {
// continue;
// }
MultipartFile file = multiRequest.getFile(name);
if (file != null && file.getSize() > 0) {
String original_filename = file.getOriginalFilename();
// String original_filename =
// files[this.fileParameterName]['originalFilename'];
String validation = validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename,
(int) file.getSize());
if ("valid".equals(validation)) {
String chunkFilename = getChunkFilename(chunkNumber, identifier);
File f = new File(chunkFilename);
if (!f.exists()) {
file.transferTo(f);
}
int currentTestChunk = 1;
int numberOfChunks = (int) Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);
currentTestChunk = this.testChunkExists(currentTestChunk, chunkNumber, numberOfChunks,
chunkFilename, original_filename, identifier, listener, "file");
} else {
listener.callback(validation, filename, original_filename, identifier, "file");
}
} else {
listener.callback("invalid_uploader_request", null, null, null, null);
}
}
}
}
private void pipeChunk(int number, String identifier, UploadOptions options, OutputStream writableStream)
throws IOException {
String chunkFilename = getChunkFilename(number, identifier);
if (new File(chunkFilename).exists()) {
FileInputStream inputStream = new FileInputStream(new File(chunkFilename));
int maxlen = 1024;
int len = 0;
try {
byte[] buff = new byte[maxlen];
while ((len = inputStream.read(buff, 0, maxlen)) > 0) {
writableStream.write(buff, 0, len);
}
} finally {
inputStream.close();
}
pipeChunk(number + 1, identifier, options, writableStream);
} else {
if (options.end)
writableStream.close();
if (options.listener != null)
options.listener.onDone();
}
}
public void write(String identifier, OutputStream writableStream, UploadOptions options) throws IOException {
if (options == null) {
options = new UploadOptions();
}
if (options.end == null) {
options.end = true;
}
pipeChunk(1, identifier, options, writableStream);
}
/**
* @param currentTestChunk
* @param chunkNumber 当前上传块
* @param numberOfChunks 总块数
* @param filename 文件名称
* @param original_filename 源文件名称
* @param identifier 文件
* @param listener 监听
* @param fileType
* @return
*/
private int testChunkExists(int currentTestChunk, int chunkNumber, int numberOfChunks, String filename,
String original_filename, String identifier, UploadListener listener, String fileType) {
String cfile = getChunkFilename(currentTestChunk, identifier);
if (new File(cfile).exists()) {
currentTestChunk++;
if (currentTestChunk >= chunkNumber) {
if (chunkNumber == numberOfChunks) {
try {
System.out.println("done");
// 文件合并
UploadOptions options = new UploadOptions();
File f = new File(this.temporaryFolder, original_filename.substring(0, original_filename.lastIndexOf("."))
+ "-" + UUIDUtil.getUUID_36() + "." + FilenameUtils.getExtension(original_filename));
options.listener = new UploadDoneListener() {
@Override
public void onError(Exception err) {
listener.callback("invalid_uploader_request", f.getAbsolutePath(), original_filename,
identifier, fileType);
clean(identifier, null);
}
@Override
public void onDone() {
listener.callback("done", f.getAbsolutePath(), original_filename, identifier, fileType);
clean(identifier, null);
}
};
this.write(identifier, new FileOutputStream(f), options);
} catch (FileNotFoundException e) {
e.printStackTrace();
listener.callback("invalid_uploader_request", filename, original_filename, identifier,
fileType);
} catch (IOException e) {
e.printStackTrace();
listener.callback("invalid_uploader_request", filename, original_filename, identifier,
fileType);
}
} else {
listener.callback("partly_done", filename, original_filename, identifier, fileType);
}
} else {
return testChunkExists(currentTestChunk, chunkNumber, numberOfChunks, filename, original_filename,
identifier, listener, fileType);
}
} else {
listener.callback("partly_done", filename, original_filename, identifier, fileType);
}
return currentTestChunk;
}
public void clean(String identifier, UploadOptions options) {
if (options == null) {
options = new UploadOptions();
}
pipeChunkRm(1, identifier, options);
}
private void pipeChunkRm(int number, String identifier, UploadOptions options) {
String chunkFilename = getChunkFilename(number, identifier);
File file = new File(chunkFilename);
if (file.exists()) {
try {
file.delete();
} catch (Exception e) {
if (options.listener != null) {
options.listener.onError(e);
}
}
pipeChunkRm(number + 1, identifier, options);
} else {
if (options.listener != null)
options.listener.onDone();
}
}
public static interface UploadListener {
public void callback(String status, String filename, String original_filename, String identifier,
String fileType);
}
public static interface UploadDoneListener {
public void onDone();
public void onError(Exception err);
}
public static class UploadOptions {
public Boolean end;
public UploadDoneListener listener;
}
}
这里面需要改两个地方
maxFileSize 设置上传的文件大小最大限制
UUIDUtil.getUUID_36() 这个是我项目自己的UUID随机生成的工具类方法,自己弄一套生成就行
util文件 UploadUtils
package com.cei.xyd_association.util;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
public class UploadUtils {
public String upload(HttpServletRequest request) {
String path = "F:/tmp/";
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
if (multipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
Iterator<String> iter = multiRequest.getFileNames();
while (iter.hasNext()) {
// 一次遍历所有文件
MultipartFile file = multiRequest.getFile(iter.next().toString());
if (file != null) {
String p = path + "/" + file.getOriginalFilename();
// 上传
try {
file.transferTo(new File(p));
return p;
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
}
自己改下自己的path就行
controller
package com.cei.xyd_association.web;
import com.cei.xyd_association.service.impl.UploadService;
import com.cei.xyd_association.util.UploadUtils;
import com.cei.xyd_association.util.Uploader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@RestController
@RequestMapping("/upload")
public class uploadController {
@Resource
private UploadService uploadService;
/**
* 分片上传
*
* @param request 前台发送过来的文件内容
* @param response 响应给前台文件路径
* @throws Exception
*/
@RequestMapping(value = "/uploadChunk", method = RequestMethod.POST)
public void upload2(HttpServletRequest request, HttpServletResponse response) throws Exception {
final String[] filepath = {""};
final String[] or_filename = {""};
try {
uploadService.post(request, new Uploader.UploadListener() {
@Override
public void callback(String status, String filename, String original_filename, String identifier, String fileType) {
if (status != null) {
System.out.println(filename);
}
filepath[0] = filename;//文件上传的路径带走
or_filename[0] = original_filename;//源文件名称带走
}
});
} catch (Exception e) {
e.printStackTrace();
}
//这句话的意思,是让浏览器用utf8来解析返回的数据
response.setHeader("Content-type", "text/html;charset=UTF-8");
//这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
response.setCharacterEncoding("UTF-8");
response.getWriter().append(filepath[0]);
}
@ResponseBody
@RequestMapping(path = "/upload2", method = RequestMethod.POST)
public void upload(HttpServletRequest request, HttpServletResponse response) {
new UploadUtils().upload(request);
this.success(response, "上传成功!");
}
public void success(HttpServletResponse response, Object obj) {
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.write(obj.toString());
} catch (Exception e) {
} finally {
if (writer != null) {
//writer.close();
}
}
}
}
这里我把 response.getWriter().append(filepath[0]); 改了,因为我们需求返回给前台能访问服务器的文件路径,所以返回文件名方便拼地址
Service文件 UploadService
package com.cei.xyd_association.service.impl;
import com.cei.xyd_association.util.Uploader;
import org.springframework.stereotype.Service;
@Service
public class UploadService extends Uploader {
public UploadService(){
super("F:/tmp/","file");
}
}
还是改自己存文件的地方
访问映射
application添加
#上传文件的路径和映射地址
#本地存放路径
localFileUrl: F:\tmp
#映射的网络url
mappingFileUrl: /static/
application的server设置
访问本地资源的启动配置
package com.cei.xyd_association.config;
import com.cei.xyd_association.Interceptor.TESTInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyWebAppConfiguration extends WebMvcConfigurerAdapter {
@Value("${localFileUrl}")
private String localFileUrl;//本地存放的文件路径 例如:E:\down
@Value("${mappingFileUrl}")
private String mappingFileUrl;//映射的虚拟网络地址 供页面连接访问的url
@Bean
public TESTInterceptor getSessionInterceptor() {
return new TESTInterceptor();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(mappingFileUrl+"**").addResourceLocations("file:"+localFileUrl+"/");
}
/**
* 拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionInterceptor())
.addPathPatterns("/**");
}
}
这个TESInterceptor是我自己的拦截器啊,实际里面没写东西,用自己的拦截器就行,我只是把代码全粘出来了没筛选
package com.cei.xyd_association.Interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TESTInterceptor implements HandlerInterceptor {
/**
* 日志对象
*/
protected static Logger logger = LoggerFactory.getLogger(TESTInterceptor.class);
/**
* 在controller后拦截
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception) throws Exception {
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView) throws Exception {
}
/**
* 在controller前拦截
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
return true;
}
}
效果图
前台切分片
后台上传分片
分片文件
最后传完合并
前台alert出文件在服务器的地址
更多推荐
所有评论(0)