前言
斷點續傳就是從文件上次中斷的地方開始重新下載或上傳,當下載或上傳文件的時候,如果沒有實現斷點續傳功能,那麼每次出現異常或者用戶主動的暫停,都會去重頭下載,這樣很浪費時間。並且對於大型文件,採用切片上傳的方法,客戶端對文件進行切片處理後,多次請求服務器,每次只傳遞一個分片。
前端
前端需要對上傳的文件進行分片處理,記錄當前上傳的文件分片的開始字節start和結束字節end,同時,整個大文件有一個md5值作爲文件的唯一標識,避免重複上傳。每次請求服務器傳遞的分片即是start到end的部分文件內容。
文件切片及上傳分片的代碼:
這裏upload是一個遞歸的函數
// 確認上傳
handleUpload() {
if (!this.previewResource) {
this.$message.warning('請先選擇需要上傳的資源!')
return
}
// 先判斷選中的資源是否已經上傳過
const data = { md5value: this.resourceForm.md5value }
this.$api.isResourceFileExist(data).then(response => {
if (response.data.exist) {
// 文件已經上傳
this.uploadExistFile()
} else {
// 文件還未上傳
this.splitFile()
}
})
},
// 文件已經上傳,不需要再上傳文件
uploadExistFile() {
let data = new FormData()
data.append('knowCateIds', this.resourceForm.knowCateIds)
data.append('title', this.resourceForm.title)
data.append('resourceType', this.resourceForm.resourceType)
data.append('brief', this.resourceForm.brief)
data.append('md5value', this.resourceForm.md5value)
data.append('name', this.resourceForm.name)
data.append('iconMd5value', this.resourceForm.iconMd5value)
this.$api
.uploadResource(data)
.then(_ => {
this.$message.success('資源上傳成功!')
this.goback()
})
.catch(_ => {
this.$message.error('資源上傳失敗!')
})
},
// 文件切片
splitFile() {
// 初始化切片
let file = this.previewResource // 需要分片上傳的文件
let splitSize = 5 * 1024 * 1024 // 每片文件的大小(5M)
let size = file.size // 文件的總大小
let start = 0 // 分片開始上傳時的大小
let end // 分片結束上傳時的大小
let index = 1 // 分片序號
let totalPieces = Math.ceil(size / splitSize) // 總分片數
// 初始化進度條
this.uploadProgress = 0
this.uploadDialogVisible = true
this.upload(start, end, index, splitSize, size, file, totalPieces)
},
// 開始上傳
upload(start, end, index, splitSize, size, file, totalPieces) {
if (start < size) {
end = start + splitSize
if (end > size) {
end = size
}
let chunk = file.slice(start, end)
let data = new FormData()
Object.keys(this.resourceForm).forEach(key => {
data.append(key, this.resourceForm[key])
})
data.append('file', chunk)
data.append('chunks', totalPieces)
data.append('chunk', index)
this.$api
.uploadResource(data)
.then(response => {
this.uploadProgress = Math.round(index / totalPieces * 100)
start = end
index++
if (index > totalPieces) {
this.$message.success('資源上傳成功!')
this.uploadDialogVisible = false
}
this.upload(start, end, index, splitSize, size, file, totalPieces)
})
.catch(_ => {
this.$message.error('資源上傳失敗!')
this.uploadDialogVisible = false
})
}
}
md5值是在選擇文件之後調用一個方法來生成的。
後端
主要思路如下:
- 先保存當前分片到臨時文件夾
- 維護一個全局的集合用來判斷當前是否是最後一個分片
- 如果是最後一個分片,則 開始合併臨時文件夾裏的文件到一個流中
- 將這個合併後的數據流輸出到一個新的文件中