Master Large File Upload with Spring Boot 3 and Vue 3: Chunked Upload Tutorial
Learn how to implement efficient large‑file uploads by splitting files into chunks, uploading them via a Vue 3 front‑end, and handling the chunked data with Spring Boot 3 back‑end APIs, including code examples for both client‑side upload logic and server‑side chunk storage and merging.
1. Introduction
File upload is a very common requirement, but uploading large files without proper control can cause several problems:
Network instability: Large uploads take a long time, and any network hiccup may cause the upload to fail, requiring a full restart.
Bandwidth limitation: In bandwidth‑constrained environments, a large upload can consume most of the available bandwidth, affecting other activities.
Server load: Processing a huge amount of data at once can overload the server, especially under high concurrency, leading to slow responses or crashes.
Chunked upload solves these issues by dividing a file into smaller parts, uploading each part separately, and then merging them on the server.
Chunked upload principle
Split the file into small chunks on the client.
Upload each chunk individually.
After all chunks are uploaded, the server merges them to reconstruct the original file.
2. Practical Example
2.1 Front‑end page
The demo page is intentionally simple, containing only three buttons to demonstrate the chunked upload feature.
Front‑end code
<code><el-upload ref="upload" class="upload-demo"
:limit="1" :auto-upload="false" :http-request="uploadFile">
<template #trigger>
<el-button type="primary" style="margin-right: 10px;">选择文件</el-button>
</template>
<el-button class="ml-3" type="success" @click="submitUpload">
上传文件
</el-button>
</el-upload>
<el-button class="ml-3" type="primary" @click="mergeFile">合并文件</el-button></code>JavaScript code
<code><script setup name="upload">
import { ref } from 'vue'
const upload = ref('')
let fileName = ''
/** Split file into 2M chunks */
const uploadFileInChunks = file => {
const chunkSize = 1024 * 1024 * 2
let start = 0
let chunkIndex = 0
while (start < file.size) {
const chunk = file.slice(start, start + chunkSize)
console.log(chunk)
fileName = file.name
uploadChunk(chunk, chunkIndex, fileName)
start += chunkSize
chunkIndex++
}
}
/** Upload a single chunk */
const uploadChunk = (chunk, chunkIndex, fileName) => {
const formData = new FormData()
formData.append('chunk', chunk)
formData.append('chunkIndex', chunkIndex)
formData.append('fileName', fileName)
fetch('http://localhost:8080/upload-chunk', {
method: 'POST',
body: formData
}).then(resp => {
console.log(resp)
})
}
const uploadFile = opt => {
uploadFileInChunks(opt.file)
}
const submitUpload = () => {
upload.value.submit()
}
/** Merge file */
const mergeFile = () => {
const formData = new FormData()
formData.append('fileName', fileName)
fetch('http://localhost:8080/merge-chunks', {
method: 'POST',
body: formData
}).then(resp => {
console.log(resp)
})
}
</script></code>The front‑end code is straightforward: it obtains the File object, splits it into chunks, and uploads each chunk.
2.2 File upload API
<code>@RestController
public class ChunkController {
private static final String TEMP_DIR = "d:\\upload\\";
@PostMapping("/upload-chunk")
public ResponseEntity<String> uploadChunk(
@RequestParam("chunk") MultipartFile chunk,
@RequestParam("chunkIndex") int chunkIndex,
@RequestParam("fileName") String fileName) throws IOException {
File dir = new File(TEMP_DIR + fileName);
if (!dir.exists()) {
dir.mkdirs();
}
File chunkFile = new File(dir, "chunk_" + chunkIndex);
try (OutputStream os = new FileOutputStream(chunkFile)) {
os.write(chunk.getBytes());
}
return ResponseEntity.ok("Chunk " + chunkIndex + " uploaded successfully.");
}
}
</code>This endpoint stores each received chunk under a temporary directory named after the original file.
2.3 Merge file API
<code>@RestController
public class ChunkController {
private static final String TEMP_DIR = "d:\\upload\\";
private static final String TARGET_DIR = "d:\\upload\\result\\";
@PostMapping("/merge-chunks")
public ResponseEntity<String> mergeChunks(@RequestParam("fileName") String fileName) throws IOException {
File dir = new File(TEMP_DIR + fileName);
File mergedFile = new File(TARGET_DIR + fileName);
try (OutputStream os = new FileOutputStream(mergedFile)) {
for (int i = 0, len = dir.listFiles().length; i < len; i++) {
File chunkFile = new File(dir, "chunk_" + i);
Files.copy(chunkFile.toPath(), os);
chunkFile.delete();
}
}
dir.delete();
return ResponseEntity.ok("文件合并完成");
}
}
</code>The merge endpoint iterates over all chunk files in order, writes them sequentially into a target file, and then cleans up the temporary files.
With these components, the application successfully implements chunked large‑file upload.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.