一、實現原理
文件分塊:將大文件切割為固定大小的塊(如5MB)
進度記錄:持久化存儲已上傳分塊信息
續傳能力:上傳中斷后根據記錄繼續上傳未完成塊
塊校驗機制:通過哈希值驗證塊完整性
合并策略:所有塊上傳完成后進行有序合并
二、前端實現(JavaScript)
// 文件分塊(示例使用Blob.slice)
// 默認 5M 一個片段
function createChunks(file, chunkSize = 5 * 1024 * 1024) {const chunks = [];let offset = 0;while (offset < file.size) {const chunk = file.slice(offset, offset + chunkSize);chunks.push({chunk,index: chunks.length,hash: file.name + '-' + chunks.length});offset += chunkSize;}return chunks;
}// 上傳控制器
class Uploader {constructor(file) {this.file = filethis.chunks = createChunks(file)this.uploaded = new Set() // 已上傳分塊索引}async checkProgress() {// 查詢服務端已上傳分塊const { data } = await axios.get('/progress', {params: { hash: this.fileHash }})this.uploaded = new Set(data.uploaded)}async upload() {await this.checkProgress()for (const chunk of this.chunks) {if (this.uploaded.has(chunk.index)) continueconst formData = new FormData()formData.append('chunk', chunk.chunk)formData.append('hash', chunk.hash)formData.append('index', chunk.index)formData.append('total', this.chunks.length)await axios.post('/upload', formData, {onUploadProgress: progress => {console.log(`塊${chunk.index}上傳進度:`, progress)}})this.uploaded.add(chunk.index)}await axios.post('/merge', { filename: this.file.name,total: this.chunks.length })}
}
三、服務端實現(Node.js + Express
)
const express = require('express')
const fs = require('fs-extra')
const path = require('path')
const app = express()// 臨時存儲目錄
const UPLOAD_DIR = path.resolve(__dirname, 'temp')// 處理分塊上傳
app.post('/upload', async (req, res) => {const { chunk, hash, index } = req.filesconst chunkDir = path.resolve(UPLOAD_DIR, hash.split('-')[0])await fs.ensureDir(chunkDir)await fs.move(chunk.path, path.resolve(chunkDir, hash))res.json({ code: 0 })
})// 合并分塊
app.post('/merge', async (req, res) => {const { filename, total } = req.bodyconst fileHash = filename + '-' + Date.now()const chunkDir = path.resolve(UPLOAD_DIR, fileHash)const destFile = path.resolve(UPLOAD_DIR, filename)// 按索引順序合并await fs.ensureDir(chunkDir)for (let i = 0; i < total; i++) {const chunkPath = path.resolve(chunkDir, `${fileHash}-${i}`)await fs.appendFile(destFile, await fs.readFile(chunkPath))await fs.unlink(chunkPath)}await fs.rmdir(chunkDir)res.json({ code: 0 })
})// 查詢上傳進度
app.get('/progress', async (req, res) => {const { hash } = req.queryconst chunkDir = path.resolve(UPLOAD_DIR, hash.split('-')[0])if (!await fs.pathExists(chunkDir)) {return res.json({ uploaded: [] })}const uploaded = (await fs.readdir(chunkDir)).map(name => parseInt(name.split('-').pop()))res.json({ uploaded })
})
四、關鍵實現步驟
分塊生成:前端使用Blob.slice
進行文件切割
唯一標識:使用"文件名+哈希值
"生成文件唯一標識
斷點記錄:
服務端保存每個文件的分塊目錄
使用Set結構
記錄已上傳分塊索引
恢復機制:
上傳前先查詢服務端上傳進度
跳過已上傳成功的分塊
合并驗證:
按索引順序合并保證文件正確性
合并完成后清理臨時分塊
五、注意事項
哈希校驗:對每個分塊計算MD5
進行完整性驗證
并發控制:前端使用Promise.all
實現并行上傳
錯誤重試:為每個分塊添加重試機制
秒傳功能:通過文件哈希值檢測服務端已存在文件
分塊大小自適應:根據網絡狀況動態調整分塊尺寸
該方案,支持TB級
文件上傳,通過分塊策略
和斷點記錄機制
可顯著提升大文件傳輸的可靠性。
實際部署時建議結合對象存儲服務實現,可進一步降低服務器存儲壓力。