ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
### 基本原理 > 利用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