以下是针对一次性上传 大文件(10G,100G)的前端方案代码,每一行都附有详细注释,解释其作用和运行逻辑。
1. 文件分片
将大文件分割成固定大小的块(如 5MB),便于分片上传。
/**
* 将文件分割成固定大小的块
* @param {File} file - 需要上传的文件
* @param {number} chunkSize - 每个分片的大小(单位:字节)
* @returns {Array<Blob>} - 分片后的文件块数组
*/
function splitFile(file, chunkSize = 5 * 1024 * 1024) {
const chunks = []; // 存储分片后的文件块
let start = 0; // 分片的起始位置
// 循环分割文件
while (start < file.size) {
// 使用 File.slice() 方法截取文件块
const chunk = file.slice(start, start + chunkSize);
chunks.push(chunk); // 将分片添加到数组中
start += chunkSize; // 更新起始位置
}
return chunks;
}
2. 并发控制
通过队列机制控制同时上传的文件数量,避免浏览器崩溃或网络阻塞。
const MAX_CONCURRENT_UPLOADS = 5; // 最大并发上传数
const uploadQueue = []; // 上传队列
let activeUploads = 0; // 当前正在上传的文件数
/**
* 将文件加入上传队列
* @param {File} file - 需要上传的文件
*/
function enqueueUpload(file) {
uploadQueue.push(file); // 将文件加入队列
processQueue(); // 处理队列中的文件
}
/**
* 处理上传队列
*/
function processQueue() {
// 如果当前上传数未达到最大并发数,并且队列中有文件
if (activeUploads < MAX_CONCURRENT_UPLOADS && uploadQueue.length > 0) {
const file = uploadQueue.shift(); // 从队列中取出一个文件
activeUploads++; // 增加当前上传数
uploadFile(file).finally(() => {
activeUploads--; // 上传完成后减少当前上传数
processQueue(); // 继续处理队列中的文件
});
}
}
3. 断点续传
在上传前查询已上传的分片,跳过已上传的部分。
/**
* 上传文件
* @param {File} file - 需要上传的文件
*/
async function uploadFile(file) {
const fileId = generateFileId(file); // 生成唯一文件 ID
const { uploadedChunks } = await getUploadedChunks(fileId); // 获取已上传的分片信息
const chunks = splitFile(file); // 分片文件
// 遍历所有分片
for (let i = 0; i < chunks.length; i++) {
// 如果分片未上传,则上传
if (!uploadedChunks.includes(i)) {
await uploadChunk(fileId, chunks[i], i); // 上传分片
}
}
await completeUpload(fileId); // 通知后端合并分片
}
4. 上传分片
使用 axios 上传分片,并监听上传进度。
/**
* 上传分片
* @param {string} fileId - 文件唯一 ID
* @param {Blob} chunk - 分片文件
* @param {number} index - 分片索引
*/
function uploadChunk(fileId, chunk, index) {
const formData = new FormData(); // 创建 FormData 对象
formData.append('file', chunk); // 添加分片文件
formData.append('fileId', fileId); // 添加文件 ID
formData.append('chunkIndex', index); // 添加分片索引
// 使用 axios 上传分片
return axios.post('/upload', formData, {
onUploadProgress: (progressEvent) => {
// 计算上传进度百分比
const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
updateProgress(fileId, index, percent); // 更新进度
}
});
}
5. 错误处理
捕获上传过程中的错误,并支持重试机制。
/**
* 上传分片(支持重试)
* @param {string} fileId - 文件唯一 ID
* @param {Blob} chunk - 分片文件
* @param {number} index - 分片索引
* @param {number} retries - 剩余重试次数
*/
async function uploadChunkWithRetry(fileId, chunk, index, retries = 3) {
try {
await uploadChunk(fileId, chunk, index); // 尝试上传分片
} catch (error) {
if (retries > 0) {
// 如果还有重试次数,则重试
return uploadChunkWithRetry(fileId, chunk, index, retries - 1);
} else {
throw error; // 重试次数用尽,抛出错误
}
}
}
6. 进度展示
实时展示每个文件的上传进度。
/**
* 更新上传进度
* @param {string} fileId - 文件唯一 ID
* @param {number} index - 分片索引
* @param {number} percent - 上传进度百分比
*/
function updateProgress(fileId, index, percent) {
const file = files.find(f => f.id === fileId); // 查找对应的文件
if (file) {
file.progress = percent; // 更新进度
renderProgress(); // 重新渲染进度条
}
}
7. 用户体验优化
支持拖拽上传和进度条展示。
<div
id="dropzone"
ondragover="event.preventDefault()"
ondrop="handleDrop(event)"
>
拖拽文件到这里
</div>
<div v-for="file in files" :key="file.id">
<div>{{ file.name }}</div>
<progress :value="file.progress" max="100"></progress>
<div v-if="file.error">
上传失败: {{ file.error }}
<button @click="retryUpload(file)">重试</button>
</div>
</div>
/**
* 处理文件拖拽事件
* @param {Event} event - 拖拽事件
*/
function handleDrop(event) {
event.preventDefault(); // 阻止默认行为
const files = event.dataTransfer.files; // 获取拖拽的文件
handleFiles(files); // 处理文件
}
/**
* 处理文件上传
* @param {FileList} files - 需要上传的文件列表
*/
function handleFiles(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
file.id = generateFileId(file); // 生成唯一文件 ID
file.progress = 0; // 初始化上传进度
enqueueUpload(file); // 加入上传队列
}
}
8. 后端接口设计
后端需要提供以下接口支持分片上传和断点续传:
- 获取已上传的分片
- 请求:
GET /uploadedChunks?fileId=123 - 响应:
{ uploadedChunks: [0, 1, 2] }
- 请求:
- 上传分片
- 请求:
POST /upload - 参数:
fileId,chunkIndex,file(分片文件)
- 请求:
- 合并分片
- 请求:
POST /completeUpload - 参数:
fileId
- 请求:
总结
通过以上代码和详细注释,可以清晰地理解如何实现一次性上传 大文件。该方案通过分片上传、并发控制、断点续传和进度展示等技术手段,确保上传过程的高效性和可靠性,同时提供友好的用户体验。