# 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. 页面效果
- 首页

- 选择文件

- 开始上传/暂停上传

- 下载/删除

## 四、技术实现
~~~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;
}
~~~