SpringBoot+Vue实现大文件上传(分片上传)
1 环境 SpringBoot 3.2.1,Vue 2,ElementUI
2 问题 前几篇文章,可以用于较小文件的上传,对于较大文件来说,为了提高上传效率和可靠性,可以采用分片上传。
3主要以下方面
资源管理:
内存消耗:上传大文件时,如果一次性读取整个文件,会占用大量内存,甚至可能导致内存溢出。而分片上传每次只读取和上传一个小块,内存消耗更可控。
带宽优化:分片上传可以更好地利用带宽资源,特别是在网络不稳定的情况下,分片上传可以避免带宽的浪费。
大文件支持:
文件大小限制:一些浏览器和服务器对单个文件的上传大小有限制。通过分片上传,可以绕过这些限制,使上传大文件成为可能。
服务器处理压力:一次性上传大文件会给服务器带来很大的压力,分片上传可以减轻服务器的负担,因为服务器可以逐片处理和存储文件。
效果图
前端代码
<template><div class="container"><el-uploadclass="upload-demo"dragaction="/xml/fileUpload"multiple:on-change="handleChange":auto-upload="false"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div></el-upload><el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button></div>
</template><script>
export default {name: 'App',data() {return {file: '',fileList: [],CHUNK_SIZE: 1024 * 1024 * 100//100MB}},watch: {},created() {},methods: {async submitUpload() {//获取上传的文件信息const file = this.fileList[0].raw//分片const totalChunks = Math.ceil(file.size / this.CHUNK_SIZE);for (let i = 0; i < totalChunks; i++) {const start = i * this.CHUNK_SIZE;const end = Math.min(start + this.CHUNK_SIZE, file.size);//将文件切片const chunk = file.slice(start, end);//组装参数const formData = new FormData();formData.append('file', chunk);formData.append('fileName', file.name);formData.append('index', i);//异步上传await fetch('/xml/bigFileUpload', {method: 'POST',body: formData});}//调用合并分片请求await fetch('/xml/merge', {method: 'POST',body: JSON.stringify({fileName: file.name}),headers: {'Content-Type': 'application/json'}});},handleChange(file, fileList) {this.fileList = fileList}}
}
</script><style>
.container {display: flex;
}
</style>
后端代码
package org.wjg.onlinexml.controller;import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.wjg.onlinexml.po.Result;import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.util.Map;@RestController
public class BigFileControll {// 获取资源文件夹的路径,路径为 项目所在路径/upload/private static final String UPLOAD_DIR = System.getProperty("user.dir") + "/upload/";/*** 保存分片* @param file* @param fileName* @param index* @return*/@RequestMapping("/bigFileUpload")private Result bigFileUpload(@RequestParam("file") MultipartFile file, @RequestParam("fileName") String fileName, @RequestParam("index") int index) {if (file.isEmpty()) {return Result.builder().code(500).msg("上传失败!").build();}File uploadDir = new File(UPLOAD_DIR);if (!uploadDir.exists()) {uploadDir.mkdirs();}File uploadFile = new File(UPLOAD_DIR + fileName + "_" + index);try {file.transferTo(uploadFile);} catch (Exception e) {e.printStackTrace();}return Result.builder().code(200).msg("上传成功").build();}/*** 合并分片* @param request* @return*/@PostMapping("/merge")public Result mergeChunks(@RequestBody Map<String, String> request) {String filename = request.get("fileName");File mergedFile = new File(UPLOAD_DIR + filename);try (FileOutputStream fos = new FileOutputStream(mergedFile)) {//循环获取分片,直到分片不存在为止for (int i = 0; ; i++) {File chunkFile = new File(UPLOAD_DIR + filename + "_" + i);if (!chunkFile.exists()) {break;}//将分片复制到一个文件中,这种方法会追加Files.copy(chunkFile.toPath(), fos);//删除分片chunkFile.delete();}} catch (Exception e) {e.printStackTrace();}return Result.builder().code(200).msg("合并成功").build();}
}