大文件分片上传
-
- 需要前端把文件分片上传(总的需要一个随机数) + 文件名 + 上传的次数
-
- 每次的分片都需要存到一个临时
随机数_文件名/文件名_上传的次数,存满了
-
- 对这些文件进行分片合并,先后顺序根据
上传的次数来处理
-
- 合并分片后删除掉临时分片文件夹
随机数_文件名
import {
Body,
Controller,
Get,
Post,
Query,
UploadedFiles,
UseInterceptors,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { FilesInterceptor } from '@nestjs/platform-express';
import * as fs from 'fs';
import { join } from 'path';
@Controller('upload')
export class UploadController {
constructor(private readonly uploadService: UploadService) {}
@Post('upload')
@UseInterceptors(
FilesInterceptor('files', 20, {
dest: 'uploads',
}),
)
async uploadFiles(
@UploadedFiles() files: Array<any>,
@Body() body: { name: string },
) {
try {
if (!files || files.length === 0) {
throw new Error('No files uploaded');
}
if (!body.name) {
throw new Error('File name is required');
}
const match = body.name.match(/(.+)\-\d+$/);
if (!match) {
throw new Error('Invalid file name format');
}
const fileName = match[1];
const chunkDir = join('uploads', 'chunks_' + fileName);
if (!fs.existsSync(chunkDir)) {
fs.mkdirSync(chunkDir, { recursive: true });
}
fs.cpSync(files[0].path, chunkDir + '/' + body.name);;
fs.rmSync(files[0].path);
return { success: true, message: 'Chunk uploaded successfully' };
} catch (error) {
console.error(error);
throw error;
}
}
@Get('merge')
merge(@Query('name') name: string) {
const chunkDir = 'uploads/chunks_' + name;
const files = fs.readdirSync(chunkDir);
let count = 0;
let startPos = 0;
files.map((file) => {
const filePath = chunkDir + '/' + file;
const stream = fs.createReadStream(filePath);
stream
.pipe(
fs.createWriteStream('uploads/' + name, {
start: startPos,
}),
)
.on('finish', () => {
count++;
if (count === files.length) {
fs.rm(
chunkDir,
{
recursive: true,
},
() => {},
);
}
});
startPos += fs.statSync(filePath).size;
});
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script>
</head>
<body>
<input id="fileInput" type="file"/>
<script>
const fileInput = document.querySelector('#fileInput');
const chunkSize = 20 * 1024;
fileInput.onchange = async function () {
const file = fileInput.files[0];
console.log(file);
const chunks = [];
let startPos = 0;
while(startPos < file.size) {
chunks.push(file.slice(startPos, startPos + chunkSize));
startPos += chunkSize;
}
const randStr = new Date().getTime();
chunks.map((chunk, index) => {
const data = new FormData();
data.set('name', randStr + '_' +file.name + '-' + index)
data.append('files', chunk);
axios.post('http://localhost:3000/upload', data);
})
}
</script>
</body>
</html>