### 基本原理
> 利用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面试
- 前端笔试题
- 笔试题