公司要做一个上传文件的功能,浏览器上传超过500m的文件会崩溃
需要切割分片上传
前端用的百度的 webuploader插件
官网 http://fex.baidu.com/webuploader/
用的springboot
打开后页面为这样
上传文件,我上传的事一个338m的文件,切割为每个32m的文件
上传后,切割为11份,
上传成功后,合并11文件为一个文件,并将11个文件删除
源码地址:
csdn https://download.csdn.net/download/qq_34874784/10743990
码云:https://gitee.com/stylesmile/spring-boot-study.git
github: https://github.com/stylesmile/spring-boot-study/tree/master/springboot-bigfile
如果你觉得对你有用,给个星星哈
项目结构为
jar 只新增了commons-fileupload这一个jar
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>springboot-bigfile</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-bigfile</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <repositories><!-- 代码库 --> <repository> <id>maven-ali</id> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>springboot, 默认限制了上传文件最大为1m,需修改,我修改为了50m
FileConfig
package com.example.springbootbigfile; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.MultipartConfigFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.MultipartConfigElement; @Configuration public class FileConfig { /** * 文件上传配置 * * @return MultipartConfigElement */ @Bean public MultipartConfigElement multipartConfigElement( @Value("${multipart.maxFileSize}") String maxFileSize, @Value("${multipart.maxRequestSize}") String maxRequestSize) { MultipartConfigFactory factory = new MultipartConfigFactory(); // 单个文件最大 factory.setMaxFileSize(maxFileSize); // 设置总上传数据总大小 factory.setMaxRequestSize(maxRequestSize); return factory.createMultipartConfig(); } }application.properties
#文件上传路径 uploadFolder=D:/data/appfiles/ #单个文件最大 multipart.maxFileSize=3000MB #设置总上传数据总大小 multipart.maxRequestSize=10000MB server.port=8088 JsonResult package com.example.springbootbigfile; /** * 描述: json格式数据返回对象,使用CustomJsonResultSerializer 来序列化 * @author : lijiazhi */ public class JsonResult<T> { private String code; private String msg; private T data; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "code=" + code + " message=" + msg + " data=" + data; } public static <T> JsonResult<T> fail() { JsonResult<T> ret = new JsonResult<T>(); ret.setCode("201"); ret.setMsg("失败"); return ret; } public static <T> JsonResult<T> fail(T data) { JsonResult<T> ret = JsonResult.fail(); ret.setData(data); return ret; } public static <T> JsonResult<T> failMessage(String msg) { JsonResult<T> ret = JsonResult.fail(); ret.setMsg(msg); return ret; } public static <T> JsonResult<T> successMessage(String msg) { JsonResult<T> ret = JsonResult.success(); ret.setMsg(msg); return ret; } public static <T> JsonResult<T> success() { JsonResult<T> ret = new JsonResult<T>(); ret.setCode("200"); ret.setMsg(""); return ret; } public static <T> JsonResult<T> success(T data) { JsonResult<T> ret = JsonResult.success(); ret.setData(data); return ret; } public static <T> JsonResult<T> http404(T data) { JsonResult<T> ret = new JsonResult<T>(); ret.setCode("404"); ret.setMsg(""); ret.setData(data); return ret; } public static <T> JsonResult<T> http403(T data) { JsonResult<T> ret = new JsonResult<T>(); ret.setCode("403"); ret.setMsg(""); ret.setData(data); return ret; } }UploadController
package com.example.springbootbigfile; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.UUID; @Controller public class UploadController { @Value("${uploadFolder}") private String filePath; private final String MODEL = "upload"; /** * 上传文件 * * @param request * @param response * @param guid * @param chunk * @param file * @param chunks */ @RequestMapping(MODEL + "/upload") public void bigFile(HttpServletRequest request, HttpServletResponse response, String guid, Integer chunk, MultipartFile file, Integer chunks) { try { boolean isMultipart = ServletFileUpload.isMultipartContent(request); if (isMultipart) { // 临时目录用来存放所有分片文件 String tempFileDir = filePath + guid; File parentFileDir = new File(tempFileDir); if (!parentFileDir.exists()) { parentFileDir.mkdirs(); } // 分片处理时,前台会多次调用上传接口,每次都会上传文件的一部分到后台 File tempPartFile = new File(parentFileDir, guid + "_" + chunk + ".part"); FileUtils.copyInputStreamToFile(file.getInputStream(), tempPartFile); } } catch (Exception e) { e.printStackTrace(); } } /** * 合并文件 * * @param guid * @param fileName * @throws Exception */ @RequestMapping(MODEL + "/merge") @ResponseBody public JsonResult mergeFile(String guid, String fileName) { // 得到 destTempFile 就是最终的文件 try { File parentFileDir = new File(filePath + guid); if (parentFileDir.isDirectory()) { File destTempFile = new File(filePath + "/merge", UUID.randomUUID() + fileName); if (!destTempFile.exists()) { //先得到文件的上级目录,并创建上级目录,在创建文件, destTempFile.getParentFile().mkdir(); try { //创建文件 destTempFile.createNewFile(); //上级目录没有创建,这里会报错 } catch (IOException e) { e.printStackTrace(); } } System.out.println(parentFileDir.listFiles().length); 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); return JsonResult.success(); } } catch (Exception e) { e.printStackTrace(); return JsonResult.fail(); } return null; } }
html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"><!--ie兼容 --> <link href="/webuploader-0.1.5/webuploader.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="/jquery.min.js"></script> <script type="text/javascript" src="/webuploader-0.1.5//webuploader.min.js"></script> </head> <body> <div id="uploader" class="wu-example"> <!--用来存放文件信息--> <div id="thelist" class="uploader-list"></div> <div class="btns"> <div id="picker">选择文件</div> <button id="ctlBtn" class="btn btn-default">开始上传</button> </div> </div> </body> <script type="text/javascript"> var GUID = WebUploader.Base.guid();//一个GUID var uploader = WebUploader.create({ // swf文件路径 swf: '/webuploader-0.1.5/Uploader.swf', // 文件接收服务端。 server: './upload/upload', formData:{ guid : GUID }, // 选择文件的按钮。可选。 // 内部根据当前运行是创建,可能是input元素,也可能是flash. pick: '#picker', chunked : true, // 分片处理 chunkSize : 2 * 1024 * 1024, // 每片2M, chunkRetry : false,// 如果失败,则不重试 threads : 1,// 上传并发数。允许同时最大上传进程数。 // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! resize: false }); $("#ctlBtn").click(function () { uploader.upload(); }); //当文件上传成功时触发。 // uploader.on( "uploadSuccess", function( file ) { // $.post('./upload/merge', { guid: GUID, fileName: file.name }, function (data) { // if(data.code == 200){ // alert('上传成功!'); // } // }); // }); </script> <script> // 当有文件被添加进队列的时候 uploader.on('fileQueued', function (file) { $("#thelist").append('<div id="' + file.id + '" class="item">' + '<h4 class="info">' + file.name + '</h4>' + '<p class="state">等待上传...</p>' + '<p class="remove-this">删除</p>' + '</div>'); //删除上传的文件 $("#thelist").on('click', '.remove-this', function () { if ($(this).parent().attr('id') == file.id) { uploader.removeFile(file); $(this).parent().remove(); } }); }); // 文件上传过程中创建进度条实时显示。 uploader.on('uploadProgress', function (file, percentage) { var $li = $('#' + file.id), $percent = $li.find('.progress .progress-bar'); // 避免重复创建 if (!$percent.length) { $percent = $('<div " class="progress progress-striped active">' + '<div class="progress-bar" style="height:20px; background-color:red; role="progressbar" style="width: 0%">' + '</div>' + '</div>').appendTo($li).find('.progress-bar'); } $li.find('p.state').text('上传中'); $percent.css('width', percentage * 100 + '%'); }); // 文件上传成功,给item添加成功class, 用样式标记上传成功。 uploader.on('uploadSuccess', function (file, response) { $('#' + file.id).find('p.state').text('已上传'); $.post('/upload/merge', { guid: GUID, fileName: file.name }, function (data) { $("#uploader").children("#" + file.id).children(".state").html("上传成功..."); //$("#uploader .state").html("上传成功..."); }); }); // 文件上传失败,显示上传出错。 uploader.on('uploadError', function (file) { $('#' + file.id).find('p.state').text('上传出错'); }); // 完成上传完了,成功或者失败,先删除进度条。 uploader.on('uploadComplete', function (file) { $('#' + file.id).find('.progress').fadeOut(); }); //所有文件上传完毕 uploader.on("uploadFinished", function () { //提交表单 }); //开始上传 $("#ctlBtn").click(function () { uploader.upload(); }); </script> </html>