主要的pom.xml:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
大家发现多了一个mysql,主要是为了在里面存文件列表用
resource目录如下:
application.properties:
替换成自己的用户名密码
pring.datasource.url=jdbc:mysql://127.0.0.1:3306/hrms?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database=mysql
#自动建表
spring.jpa.hibernate.ddl-auto=update
#设置数据库方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#打印sql
spring.jpa.show-sql=true
#连接mongodb
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/mydb
spring.data.mongodb.username=root
spring.data.mongodb.password=123456
前端upload.html:
进度条仅为测试,样式有点丑请忽略(webUploader官网的例子有点问题,需要自己调一下)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--引入CSS-->
<link rel="stylesheet" type="text/css" href="/css/webuploader.css">
<!--引入JS-->
<script type="text/javascript" src="/js/admin.js"></script>
<script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/js/webuploader.js"></script>
<!--SWF在初始化的时候指定,在后面将展示-->
<style>
#ctlBtn {
top: 0px;
left: 0px;
width: 94px;
height: 40.8px;
overflow: hidden;
bottom: auto;
right: auto;
}
</style>
</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>
var md5;
//监听分块上传过程中的三个时间点
WebUploader.Uploader.register({
"before-send-file": "beforeSendFile",
"before-send": "beforeSend",
"after-send-file": "afterSendFile"
}, {
//时间点1:所有分块进行上传之前调用此函数
beforeSendFile: function (file) {
var deferred = WebUploader.Deferred();
//1、计算文件的唯一标记,用于断点续传
(new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024)
.progress(function (percentage) {
$('#' + file.id).find("p.state").text("正在读取文件信息...");
})
.then(function (val) {
md5 = val;
$('#' + file.id).find("p.state").text("成功获取文件信息...");
//获取文件信息后进入下一步
deferred.resolve();
});
return deferred.promise();
},
//时间点2:如果有分块上传,则每个分块上传之前调用此函数
beforeSend: function (block) {
var deferred = WebUploader.Deferred();
$.ajax({
type: "get",
url: BASE_PATH + "/check",
data: {
//文件唯一标记
"md5": md5,
//当前分块下标
"chunk": block.chunk,
//当前分块大小
"chunkSize": block.end - block.start
},
//无缓存
cache: false,
//无异步(必须加,否则异步上传会上传已经上传过的块)
async: false,
dataType: "json",
success: function (data) {
if (data) {
//分块存在,跳过
deferred.reject();
} else {
//分块不存在或不完整,重新发送该分块内容
deferred.resolve();
}
}
});
this.owner.options.formData.md5 = md5;
deferred.resolve();
return deferred.promise();
}
//时间点3:所有分块上传成功后调用此函数
// afterSendFile: function () {
// //如果分块上传成功,则通知后台合并分块
// $.ajax({
// type: "POST",
// url: "<%=basePath%>Video?action=mergeChunks",
// data: {
// md5: md5,
// },
// success: function (response) {
// alert("上传成功");
// var path = "uploads/" + md5 + ".mp4";
// $("#item1").attr("src", path);
// }
// });
// }
});
var uploader = WebUploader.create({
// swf文件路径
swf: BASE_PATH + "/js/Uploader.swf",
// 文件接收服务端。
server: BASE_PATH + "/upload",
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: "#picker",
// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
resize: false,
//开启分片上传
chunked: true,
//分片大小
chunkSize: 10 * 1024 * 1024,
//并发数
threads: 10
//请求参数表
// formData:datalist,
});
$("#ctlBtn").on("click", function () {
uploader.upload();
});
// 当有文件被添加进队列的时候
uploader.on('fileQueued', function (file) {
$("#thelist").append("<div id=\"" + file.id + "\" class=\"item\">" +
"<h4 class=\"info\">" + file.name + "</h4>" +
"<p class=\"state\">等待上传...</p>" +
"</div>");
// //暂停上传的文件
// $("#thelist").on('click', '.stop', function () {
// uploader.stop(true);
// }),
// //删除上传的文件
// $("#thelist").on('click', '.remove', function () {
// if ($(this).parents(".item").attr('id') === file.id) {
// uploader.removeFile(file);
// $(this).parents(".item").remove();
// }
// })
});
// 文件上传过程中创建进度条实时显示。
uploader.on('uploadProgress', function (file, percentage) {
var $li = $('#' + file.id);
var $percent = $li.find('.progress .progress-bar');
// 避免重复创建
if (!$percent.length) {
// $percent = $('<div class="progress progress-striped active">' +
// '<div class="progress-bar" role="progressbar" style="width: 0%">' +
// '</div>' +
// '</div>').appendTo($li).find('.progress-bar');
$percent = $('<div class="progress progress-striped active">' +
'<div class="progress-bar" role="progressbar" style="width: 0%">' +
'</div>' +
'</div>').appendTo($li).find('.progress');
}
$li.find('p.state').text('上传中');
$percent.css({'width': percentage * 100 + '%', 'height': 10, 'background-color': 'blue'});
});
uploader.on('uploadSuccess', function (file) {
$('#' + file.id).find('p.state').text('已上传');
});
uploader.on('uploadError', function (file) {
$('#' + file.id).find('p.state').text('上传出错');
});
uploader.on('uploadComplete', function (file) {
$('#' + file.id).find('.progress').fadeOut();
});
</script>
</html>
前端download.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>download</title>
<script type="text/javascript" src="/js/admin.js"></script>
<script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script>
</head>
<body>
<div id="fileList">
</div>
</body>
<script type="text/javascript">
function download(id) {
window.location.href = BASE_PATH + "/download?md5=" + id;
}
$.ajax({
url: BASE_PATH + "/fileList",
type: "get",
success: function (data) {
$.each(data, function (index, val) {
var html = "<p>" + val.name + "</p><button οnclick=\"download('" + val.id + "')\">下载</button>";
$("#fileList").append(html);
})
}
})
</script>
</html>
存BASE_PATH的admin.js:
var obj = window.document.location;
var BASE_PATH = obj.href.substring(0, obj.href.indexOf(obj.pathname));
最重要的后端FileController:
文件上传至gridfs,直接存的块
package com.example.demo.controller;
import com.example.demo.entity.FileEntity;
import com.example.demo.jpa.FileJpa;
import com.mongodb.BasicDBObject;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
/**
* @author FastKing
* @version 1.0
* @date 2018/10/26 9:56
**/
@RestController
public class FileController {
@Autowired
private MongoDbFactory mongoDbFactory;
@Autowired
private FileJpa fileJpa;
@GetMapping("/fileList")
public Object list() {
return fileJpa.findAll();
}
@PostMapping("/upload")
public void upload(@RequestParam(value = "file") MultipartFile file,
@RequestParam(value = "md5") String md5,
@RequestParam(value = "chunk", defaultValue = "0", required = false) String chunk,
@RequestParam(value = "name") String name) {
FileEntity fileEntity = new FileEntity();
fileEntity.setId(md5);
fileEntity.setName(name);
if (!fileJpa.findOne(Example.of(fileEntity)).isPresent()) {
fileJpa.save(fileEntity);
}
BasicDBObject basicDBObject = new BasicDBObject();
//块下标,下载文件时需要合并块,用这个参数排序
basicDBObject.append("chunk", Short.valueOf(chunk));
//块大小,断点续传时,用这个参数判断该块是否完整上传
basicDBObject.append("chunkSize", file.getSize());
//文件唯一标识,用来筛选对应文件的所有块
basicDBObject.append("md5", md5);
GridFS gridFS = new GridFS(mongoDbFactory.getLegacyDb());
try {
GridFSInputFile inputFile = gridFS.createFile(file.getInputStream(), name.substring(0, name.lastIndexOf(".")));
//设置文件类型
inputFile.setContentType(name.substring(name.lastIndexOf(".") + 1));
//存入元数据
inputFile.setMetaData(basicDBObject);
inputFile.save();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/download")
public void download(@RequestParam(value = "md5") String md5, HttpServletResponse response) {
//1.设置文件ContentType类型,这样设置,会自动判断下载文件类型
response.setContentType("text/html;charset=utf-8");
response.setContentType("application/x-download");
response.addHeader("Cache-Control", "no-cache");
GridFS gridFS = new GridFS(mongoDbFactory.getLegacyDb());
//两个BasicDBObject,第一个为筛选条件,第二个为排序(-1为DESC倒序,1为ASC正序)
List<GridFSDBFile> gridFSDBFiles = gridFS.find(new BasicDBObject("metadata.md5", md5), new BasicDBObject("metadata.chunk", 1));
byte[] buffer = new byte[1024];
try {
ServletOutputStream outputStream = response.getOutputStream();
Long lenth = 0L;
for (GridFSDBFile gridFSDBFile : gridFSDBFiles) {
//2.设置文件头:
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(gridFSDBFile.getFilename() + "." + gridFSDBFile.getContentType(), "utf-8"));
lenth += gridFSDBFile.getLength();
int count;
InputStream inputStream = gridFSDBFile.getInputStream();
while ((count = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);
}
inputStream.close();
}
response.setHeader("Content-Length", "" + lenth);
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/check")
public Object check(@RequestParam(value = "md5") String md5,
@RequestParam(value = "chunk", required = false) String chunk,
@RequestParam(value = "chunkSize", required = false) String chunkSize) {
GridFS gridFS = new GridFS(mongoDbFactory.getLegacyDb());
BasicDBObject basicDBObject = new BasicDBObject();
//根据文件唯一标识和块下标确定唯一一个块
basicDBObject.append("metadata.md5", md5);
basicDBObject.append("metadata.chunk", Short.valueOf(chunk));
GridFSDBFile file = gridFS.findOne(basicDBObject);
//判断该块是否上传过,或是否上传完整
if (null != file && Short.valueOf(file.getMetaData().get("chunkSize").toString()).equals(Short.valueOf(chunkSize))) {
return true;
} else if (null != file) {
//如果上传不完整则重新上传
gridFS.remove(basicDBObject);
}
return false;
}
}