高效分片上传-架构图
tip 示例代码
前端模块
<template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="uploadFile">开始上传</button>
<div v-for="progress in uploadProgress" :key="progress.id">
文件名: {{ progress.name }},进度: {{ progress.progress }}
</div>
</div>
</template>
<script>
import { ref } from 'vue';
import axios from 'axios';
export default {
data() {
return {
file: null,
uploadProgress: []
};
},
methods: {
handleFileChange(event) {
this.file = event.target.files[0];
},
uploadFile() {
const chunkSize = 5 * 1024 * 1024; // 分片大小为5MB,可根据需求调整
const fileSize = this.file.size;
const totalChunks = Math.ceil(fileSize / chunkSize);
let postAll = [];
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, fileSize);
const chunk = this.file.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
postAll.push(axios.post('http://localhost:7076/uploadFile', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: (event) => {
const progress = Math.round((start + event.loaded) / fileSize * 100);
this.uploadProgress[i] = { id: i, name: this.file.name, progress };
}
}))
}
console.log("totalChunks>>>>:" + totalChunks)
Promise.all(totalChunks)
.then((responses) => {
console.log("处理完成")
})
.catch((error) => {
// 处理错误
console.error(error);
});
}
// 使用 Promise.all 处理这些请求
}
};
</script>
后端模块
@RestController
@Slf4j
public class UploadController {
private static final ReentrantLock LOCK = new ReentrantLock(true);
private static final Executor EXECUTOR = new ThreadPoolExecutor(12, 12, 5,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.DiscardOldestPolicy());
private static String path = "E:\\temp";
private static Integer num = 0;
@PostMapping("uploadFile")
public void uploadFile(MultipartFile file) throws InterruptedException, IOException {
File dirPath = new File(path);
if (!dirPath.exists()) {
dirPath.mkdir();
}
String originalFilename = file.getOriginalFilename();
CompletableFuture<Void> completableFutureOne = CompletableFuture.runAsync(() -> {
String test = UUID.randomUUID().toString() + ".mp4";
String newPath = path + "\\" + test;
try {
file.transferTo(new File(newPath));
} catch (IOException e) {
e.printStackTrace();
}
}, EXECUTOR);
CompletableFuture.allOf(completableFutureOne)
.whenComplete((v, th) ->
{
num++;
log.info("文件存储成功, ====> {}", System.currentTimeMillis());
}
).join();
}
@GetMapping("getNum")
public Integer getNum() throws InterruptedException, IOException {
return num;
}
}