# webuploader_demo **Repository Path**: zhangyingtao/webuploader_demo ## Basic Information - **Project Name**: webuploader_demo - **Description**: SpringBoot2.0整合WebUploader - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 17 - **Forks**: 7 - **Created**: 2020-03-06 - **Last Updated**: 2023-11-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SpringBoot 2.0 整合 WebUploader插件 ## 一、项目介绍 ​ 本项目基于SpringBoot2.0构建,使用Thymeleaf视图解析器,前端使用bootstrap+WebUploader ## 二、技术介绍 * springboot官网:https://spring.io/projects/spring-boot/ * thymeleaf官网:https://www.thymeleaf.org/ * webuploader官网:https://fex.baidu.com/webuploader/ * bootstrap官网:https://www.bootcss.com/ ## 三、页面效果 1. 下载项目,启动并访问:http://localhost:8080/upload/index 2. 页面效果 - 首页 ![首页](https://github.com/zyt1272999061/webuploader_demo/blob/master/images/index.bmp) - 选择文件 ![等待上传](https://github.com/zyt1272999061/webuploader_demo/blob/master/images/paused.bmp) - 开始上传/暂停上传 ![暂停上传](https://github.com/zyt1272999061/webuploader_demo/blob/master/images/waiting.bmp) - 下载/删除 ![下载](https://github.com/zyt1272999061/webuploader_demo/blob/master/images/download.bmp) ## 四、技术实现 ~~~flow ```flow st=>start: 开始 op=>operation: 选择文件 op1=>operation: 将文件分片并上传每个分片到服务器 op2=>operation: 所有分片上传成功后,通知服务器合并分片 e=>end st->op->op1->op2()->e &``` ~~~ ## 五、代码实现 1. pom.xml ```xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.6.RELEASE com.github.zyt webuploader 0.0.1-SNAPSHOT webuploader Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-devtools runtime org.projectlombok lombok 1.18.12 org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin org.springframework.boot spring-boot-maven-plugin true ``` 2. application.properties ```properties server.port=8080 server.tomcat.max-threads=6 server.tomcat.min-spare-threads=3 server.tomcat.accept-count=10 server.tomcat.max-connections=1000 # thymeleaf spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.servlet.content-type=text/html spring.thymeleaf.cache=false #开启静态资源扫描 spring.mvc.static-path-pattern=/** #开启热部署 spring.devtools.restart.enabled=true #设置最大上传大小 spring.servlet.multipart.enabled=true spring.servlet.multipart.max-file-size=5GB spring.servlet.multipart.max-request-size=5GB #文件路径 upload_path=D:\\upload ``` 3. 上传下载页面 * 引入css、js ```html ``` 4. 后端代码 * 校验 ```java /** * 查看当前分片是否上传 * * @param request * @param response */ @PostMapping("checkblock") @ResponseBody public void checkMd5(HttpServletRequest request, HttpServletResponse response) { //当前分片 String chunk = request.getParameter("chunk"); //分片大小 String chunkSize = request.getParameter("chunkSize"); //当前文件的MD5值 String guid = request.getParameter("guid"); //分片上传路径 String tempPath = uploadPath + File.separator + "temp"; File checkFile = new File(tempPath + File.separator + guid + File.separator + chunk); response.setContentType("text/html;charset=utf-8"); try { //如果当前分片存在,并且长度等于上传的大小 if (checkFile.exists() && checkFile.length() == Integer.parseInt(chunkSize)) { response.getWriter().write("{\"ifExist\":1}"); } else { response.getWriter().write("{\"ifExist\":0}"); } } catch (IOException e) { e.printStackTrace(); } } ``` * 上传分片 ```java /** * 上传分片 * * @param file * @param chunk * @param guid * @throws IOException */ @PostMapping("save") @ResponseBody public void upload(@RequestParam MultipartFile file, Integer chunk, String guid) throws IOException { String filePath = uploadPath + File.separator + "temp" + File.separator + guid; File tempfile = new File(filePath); if (!tempfile.exists()) { tempfile.mkdirs(); } RandomAccessFile raFile = null; BufferedInputStream inputStream = null; if (chunk == null) { chunk = 0; } try { File dirFile = new File(filePath, String.valueOf(chunk)); //以读写的方式打开目标文件 raFile = new RandomAccessFile(dirFile, "rw"); raFile.seek(raFile.length()); inputStream = new BufferedInputStream(file.getInputStream()); byte[] buf = new byte[1024]; int length = 0; while ((length = inputStream.read(buf)) != -1) { raFile.write(buf, 0, length); } } catch (Exception e) { throw new IOException(e.getMessage()); } finally { if (inputStream != null) { inputStream.close(); } if (raFile != null) { raFile.close(); } } } ``` * 合并分片 ```java /** * 合并文件 * * @param guid * @param fileName */ @PostMapping("combine") @ResponseBody public void combineBlock(String guid, String fileName) { //分片文件临时目录 File tempPath = new File(uploadPath + File.separator + "temp" + File.separator + guid); //真实上传路径 File realPath = new File(uploadPath + File.separator + "real"); if (!realPath.exists()) { realPath.mkdirs(); } File realFile = new File(uploadPath + File.separator + "real" + File.separator + fileName); FileOutputStream os = null;// 文件追加写入 FileChannel fcin = null; FileChannel fcout = null; try { log.info("合并文件——开始 [ 文件名称:" + fileName + " ,MD5值:" + guid + " ]"); os = new FileOutputStream(realFile, true); fcout = os.getChannel(); if (tempPath.exists()) { //获取临时目录下的所有文件 File[] tempFiles = tempPath.listFiles(); //按名称排序 Arrays.sort(tempFiles, (o1, o2) -> { if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) { return -1; } if (Integer.parseInt(o1.getName()) == Integer.parseInt(o2.getName())) { return 0; } return 1; }); //每次读取10MB大小,字节读取 //byte[] byt = new byte[10 * 1024 * 1024]; //int len; //设置缓冲区为10MB ByteBuffer buffer = ByteBuffer.allocate(10 * 1024 * 1024); for (int i = 0; i < tempFiles.length; i++) { FileInputStream fis = new FileInputStream(tempFiles[i]); /*while ((len = fis.read(byt)) != -1) { os.write(byt, 0, len); }*/ fcin = fis.getChannel(); if (fcin.read(buffer) != -1) { buffer.flip(); while (buffer.hasRemaining()) { fcout.write(buffer); } } buffer.clear(); fis.close(); //删除分片 tempFiles[i].delete(); } os.close(); //删除临时目录 if (tempPath.isDirectory() && tempPath.exists()) { System.gc(); // 回收资源 tempPath.delete(); } log.info("文件合并——结束 [ 文件名称:" + fileName + " ,MD5值:" + guid + " ]"); } } catch (Exception e) { log.error("文件合并——失败 " + e.getMessage()); } } ``` * 查询上传目录下文件 ~~~java /** * 查询上传目录下的全部文件 * * @return */ @GetMapping("/getFiles") @ResponseBody public Map getFiles() { Map map = new HashMap(); String realUploadPath = uploadPath + File.separator + "real"; File directory = new File(realUploadPath); File[] files = directory.listFiles(); List fileList = new ArrayList<>(); if (null != files && files.length > 0) { for (File file : files) { fileList.add(new FileEntity(file.getName(), getDate(file.lastModified()), file.getName().substring(file.getName().lastIndexOf(".")), Math.round(file.length() / 1024) + "KB")); } } map.put("fileList", fileList); return map; } ~~~ * 文件下载 ~~~java /** * 文件下载 * * @param fileName 文件名称 * @param response HttpServletResponse */ @GetMapping("downloadFile") @ResponseBody public void downLoadFile(String fileName, HttpServletResponse response) { File file = new File(uploadPath + File.separator + "real" + File.separator + fileName); if (file.exists()) { InputStream is = null; OutputStream os = null; try { response.reset(); // 设置强制下载不打开 response.setContentType("application/force-download"); //设置下载文件名 response.addHeader("Content-Disposition", "attachment;filename=" + fileName); response.addHeader("Content-Length", "" + file.length()); //定义输入输出流 os = new BufferedOutputStream(response.getOutputStream()); is = new BufferedInputStream(new FileInputStream(file)); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) > 0) { os.write(buffer, 0, len); os.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } log.info("文件下载成功——文件名:" + fileName); } } } ~~~ * 文件删除 ~~~java /** * 删除文件 * * @param fileName * @return */ @GetMapping("/delFile") @ResponseBody public Map delFile(String fileName) { boolean b = false; File file = new File(uploadPath + File.separator + "real" + File.separator + fileName); if (file.exists() && file.isFile()) { b = file.delete(); } Map map = new HashMap(); map.put("result", b + ""); return map; } ~~~