前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css">
<style>
.progress {
height: 20px;
margin-bottom: 20px;
overflow: hidden;
background-color: #f5f5f5;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
}
.progress-bar {
float: left;
width: 0;
height: 100%;
font-size: 12px;
line-height: 20px;
color: #fff;
text-align: center;
background-color: #337ab7;
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
-webkit-transition: width .6s ease;
transition: width .6s ease;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2 mt-5">
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="file">选择文件</label>
<input type="file" id="file" name="file" class="form-control-file">
</div>
<div class="progress">
<div class="progress-bar" role="progressbar"></div>
</div>
<button type="button" class="btn btn-primary" onclick="upload()">上传</button>
<button type="button" class="btn btn-danger" onclick="cancel()">取消</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
var xhr;
function upload() {
var file = $('#file')[0].files[0];
var filename = file.name;
var chunkSize = 1024 * 1024; // 1MB
var totalSize = file.size;
var totalChunks = Math.ceil(totalSize / chunkSize);
var chunkNumber = 0;
var progressBar = $('.progress-bar');
// 显示上传进度条
progressBar.css('width', '0%').attr('aria-valuenow', 0);
// 分片上传文件
function uploadChunk() {
if (xhr) {
xhr.abort();
}
xhr = new XMLHttpRequest();
var start = chunkNumber * chunkSize;
var end = Math.min(start + chunkSize, totalSize);
var chunk = file.slice(start, end);
var formData = new FormData();
formData.append('file', chunk);
formData.append('filename', filename);
formData.append('chunkNumber', chunkNumber);
formData.append('totalChunks', totalChunks);
xhr.open('post', '/upload');
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
var percent = Math.round((start + e.loaded) / totalSize * 100);
progressBar.css('width', percent + '%').attr('aria-valuenow', percent);
}
};
xhr.onload = function () {
chunkNumber++;
if (chunkNumber < totalChunks) {
uploadChunk();
} else {
// 所有分片上传完成
alert('上传完成');
}
};
xhr.send(formData);
}
// 开始上传
uploadChunk();
}
function cancel() {
if (xhr) {
xhr.abort();
$('.progress-bar').css('width', '0%').attr('aria-valuenow', 0);
alert('上传已取消');
}
}
</script>
</body>
</html>
后端代码
@RestController
public class FileUploadController {
@Autowired
private ServletContext servletContext;
@PostMapping("/upload")
public ResponseEntity<?> upload(@RequestParam("file") MultipartFile multipartFile,
@RequestParam("chunkNumber") int chunkNumber,
@RequestParam("totalChunks") int totalChunks,
@RequestParam("filename") String filename) throws IOException {
// 获取上传目录
String uploadPath = servletContext.getRealPath("/uploads");
// 检查上传目录是否存在,如果不存在则创建
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 获取分片文件的存储路径
String filePath = uploadPath + File.separator + filename + ".part" + chunkNumber;
// 保存分片文件
File partFile = new File(filePath);
multipartFile.transferTo(partFile);
// 如果所有分片都已上传,则合并文件
if (chunkNumber == totalChunks - 1) {
String destPath = uploadPath + File.separator + filename;
mergeFiles(uploadDir, filename, destPath, totalChunks);
}
return ResponseEntity.ok().build();
}
/**
* 合并文件
*
* @param dir 分片文件所在目录
* @param filename 文件名
* @param destPath 合并后的文件路径
* @param totalChunks 总分片数
* @throws IOException
*/
private void mergeFiles(File dir, String filename, String destPath, int totalChunks) throws IOException {
File destFile = new File(destPath);
OutputStream out = new FileOutputStream(destFile);
byte[] buffer = new byte[1024];
int len;
for (int i = 0; i < totalChunks; i++) {
File partFile = new File(dir, filename + ".part" + i);
InputStream in = new FileInputStream(partFile);
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
}
out.close();
}
}