### 基本原理
> 利用H5,FileReader,Splice的方法,可以分片讀取文件流
>
### 代碼
```js
// sparkmd5 根據文件流生成文件的唯一md5標識
import SparkMD5 from 'spark-md5'
// 定義一些狀態
const STATUS = {
READ_SUCCESS: 1,
READ_FAIL: 2,
UPLOAD_SUCCESS: 101,
UPLOAD_FAIL: 102
}
// 文件分片上傳實體類
export default class FileUploader {
constructor(file, options) {
console.log('init Uploader')
this.file = file
this.opt = Object.assign(
{
chunkSize: Math.pow(1024, 2) * 5,
url: '/upload',
progress: function() {},
complete: function() {},
clearable: true
},
options || {}
)
// 根據文件大小計算出文件被切分的份數
this.chunks = Math.ceil(this.file.size / this.opt.chunkSize)
console.log(`chunks:${this.chunks}`)
// 很關鍵,File原型上的切分方法
this.blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice
this.file.md5 = ''
}
run() {
this.md5File().then(res => {
let start = this.getCurrentChunk(this.file.md5)
this.upload(start)
})
}
// md5文件的方法
md5File() {
return new Promise((resolve, reject) => {
let currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader()
fileReader.onload = e => {
this.opt.progress(
STATUS.READ_SUCCESS,
Math.floor((currentChunk / this.chunks) * 100)
)
spark.append(e.target.result) // Append array buffer
currentChunk++
if (currentChunk < this.chunks) {
this.loadNext(currentChunk, fileReader)
} else {
console.log('finished loading')
let result = spark.end()
this.file.md5 = result
this.opt.progress(STATUS.READ_SUCCESS, 100)
resolve(result)
}
}
fileReader.onerror = function() {
console.warn('oops, something went wrong.')
this.opt.progress(STATUS.READ_FAIL)
}
this.loadNext(currentChunk, fileReader)
})
}
// 我們對正在上傳的片區做本地持久化緩存
getCurrentChunk(md5) {
let fileChunkPosition = JSON.parse(localStorage.getItem(md5))
if (!fileChunkPosition) {
fileChunkPosition = {
time: Date.now(),
chunk: 0
}
localStorage.setItem(md5, JSON.stringify(fileChunkPosition))
}
return fileChunkPosition.chunk
}
upload(index) {
// console.log('起始下標:' + index)
this.sendToServer(index)
.then(res => {
if (res.success) {
if (index == this.chunks - 1) {
if (this.opt.clearable) {
localStorage.removeItem(this.file.md5)
}
this.opt.progress(STATUS.UPLOAD_SUCCESS, 100)
this.opt.complete(this.file)
return
}
let radio = Math.floor(((index + 1) / this.chunks) * 100)
this.opt.progress(STATUS.UPLOAD_SUCCESS, radio)
localStorage.setItem(
this.file.md5,
JSON.stringify({
time: Date.now(),
chunk: res.index + 1
})
)
index++
this.upload(index)
}
})
.catch(e => {
this.opt.progress(STATUS.UPLOAD_FAIL)
})
}
// 使用formdata對圖片進行上傳
sendToServer(index) {
return new Promise((resolve, reject) => {
let startSliceSize = index * this.opt.chunkSize,
nextSliceSize = (index + 1) * this.opt.chunkSize
let endSliceSize =
nextSliceSize >= this.file.size ? this.file.size : nextSliceSize
console.log(
`切割${startSliceSize / 1024 / 1024}MB ~ ${endSliceSize /
1024 /
1024}MB`
)
let form = new FormData()
form.append('file', this.file.slice(startSliceSize, endSliceSize))
form.append('fileName', this.file.name)
form.append('total', this.chunks) //總片數
form.append('index', index + 1) //當前是第幾片
form.append('fileMd5Value', this.file.md5)
form.append('catalogid', this.opt.parentId)
var xhr = new XMLHttpRequest()
xhr.open('POST', this.opt.url, true)
xhr.onload = e => {
let target = e.target
if (target.status == 200) {
resolve({
success: true,
index: index
})
} else {
reject()
}
}
xhr.onerror = e => {
reject()
}
xhr.send(form)
})
}
// 分片上傳完成 通知服務器對文件進行合并
notifyServer() {
let url = `/merge?md5=${this.file.md5}&fileName=${this.file.name}&size=${
this.file.size
}`
$.getJSON(url, function(data) {
alert('上傳成功')
})
}
loadNext(currentChunk, fileReader) {
var start = currentChunk * this.opt.chunkSize,
end =
start + this.opt.chunkSize >= this.file.size
? this.file.size
: start + this.opt.chunkSize
fileReader.readAsArrayBuffer(this.blobSlice.call(this.file, start, end))
}
}
```
### 參考
1. http://www.zuidaima.com/blog/2819949848316928.htm
2. http://fex.baidu.com/webuploader/
3. https://segmentfault.com/a/1190000002992032
4. https://segmentfault.com/a/1190000000725971
- 前端
- C1-Javascript
- H5圖片分塊和斷點續傳
- JavascriptPatterns[Stoyanstefanov]
- macotask和microtask
- 前端代碼生成器
- 跨域
- 頁面回到頂部滾動按鈕實現
- C2-CSS
- 瀏覽器的一些單位
- 盒模型
- 移動端判斷橫豎屏
- C3-框架
- ReactNative
- 開發環境搭建(安卓篇)
- Vue
- vue+pdfjs使用
- vue+typescript使用實踐
- vue+webpack3.x集成typescript
- Vue源碼3
- vue源碼分析1
- vue源碼分析2
- vue筆記
- C4-工具
- git
- Gitlab-CICD
- mock規則
- vscode-settings
- webpack自定義命令,切換代理地址
- 正則表達式
- 深入淺出webpack
- C5-Node
- express
- express源碼閱讀
- nightmare使用指南
- 爬蟲1.0
- C6-微信
- 微信
- C7-Canvas
- 基礎API
- 前端隨筆筆記
- 后端
- C1-Java
- shiro
- C2-Linux
- ffmpeg
- ITerm
- Linux
- MongoDB安裝
- MySql安裝
- Ngnix反向代理
- 常見錯誤
- 備忘
- mac
- 備忘-Work
- 備忘Link
- 服務器資源
- 教程
- Hexo個人博客搭建筆錄
- 文檔
- CSS編碼規范
- 前端編碼規范
- 隨筆
- 整理
- 正則
- 鏈接收藏
- 面試
- CodeWars題庫
- CodeWars題庫(二)
- Java社招面試題
- Java面試
- Web面試
- 前端筆試題
- 筆試題