vue3:
<template>
<div>
<h1>大文件分片上传</h1>
<input type="file" @change="onFileChange"/>
<div v-if="progress > 0">
上传进度: {{ progress }}%
</div>
</div>
</template>
<script setup>
import axios from 'axios'
import {ref} from 'vue'
const file = ref(null)
const progress = ref(0)
const onFileChange = (event) => {
file.value = event.target.files[0]
if (file.value) {
uploadFile()
}
}
const uploadFile = async () => {
progress.value = 0
const chunkSize = 1 * 1024 * 1024 // 1MB 分片
const totalChunks = Math.ceil(file.value.size / chunkSize)
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const start = chunkIndex * chunkSize
const end = Math.min(start + chunkSize, file.value.size)
const chunk = file.value.slice(start, end)
const formData = new FormData()
formData.append('file', chunk)
formData.append('chunkIndex', chunkIndex)
formData.append('totalChunks', totalChunks)
formData.append('fileName', file.value.name)
await axios.post('http://localhost:3000/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
progress.value = Math.round(((chunkIndex + 1) / totalChunks) * 100)
}
// await axios.post('http://localhost:3000/merge', {
// filename: this.selectedFile.name,
// totalChunks,
// });
alert('文件上传成功!');
}
</script>
express.js:
const express = require('express');
const multer = require('multer');
const fs = require('fs-extra');
const path = require('path');
const cors = require('cors');
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.json());
const upload = multer({dest: 'uploads/'});
app.post('/upload', upload.single('file'), async (req, res) => {
const {originalname} = req.file;
const {chunkIndex, totalChunks, fileName} = req.body;
const chunkDir = path.join(__dirname, 'uploads', fileName.split('.')[0] + '_' + fileName.split('.')[1]);
try {
await fs.ensureDir(chunkDir);
const chunkPath = path.join(chunkDir, chunkIndex);
await fs.move(req.file.path, chunkPath);
if (parseInt(chunkIndex) + 1 == parseInt(totalChunks)) {
await mergeChunks(chunkDir, fileName);
res.send({message: `Chunk ${chunkIndex} uploaded.Upload complete.`});
} else {
res.send({message: `Chunk ${chunkIndex} uploaded.`});
}
} catch (err) {
console.error(err);
res.status(500).send({message: 'Upload failed.', error: err.message});
}
});
async function mergeChunks(chunkDir, fileName) {
const filePath = path.join(__dirname, 'uploads', fileName);
const chunkPaths = await fs.readdir(chunkDir);
chunkPaths.sort((a, b) => parseInt(a) - parseInt(b));
for (let chunkPath of chunkPaths) {
const chunkFullPath = path.join(chunkDir, chunkPath);
await fs.appendFile(filePath, await fs.readFile(chunkFullPath));
}
await fs.remove(chunkDir);
}
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
后端(Express.js):处理文件上传、分片存储和文件合并。
前端(Vue 3 + Axios):实现文件分片上传,并与后端进行通信。
分片上传:文件被切分为多个小块上传,后端在接收到所有分片后合并为完整的文件。