Vue 页面中使用 ElementUI 实现一个上传,上传之前先计算一下文件的哈希(MD5)值,然后调用后端接口查看文件在服务器中是否存在,如果没有文件哈希记录,上传文件;如果存在,只修改数据库记录,不进行实际文件上传。
前端在浏览器中实现对文件操作,不像后端用个现成的工具包,两三行代码就可以搞定,实现思路是通过 FileReader 把文件读取到内存,获取文件二进制,再进行 md5 加密。
这里的功能实现使用 js-spark-md5 包,据说使用了世界上最快的 md5 算法,官方提供的例子以及文档很详细。
使用 Vue ElementUI 实现有个坑需要注意一下
on-change
事件中的获取 file
对象不能直接在 blobSlice.call()
中使用,需要使用 file
的原生对象,否则会导致 illegal invocation
错误
ElementUI on-change
事件函数中获取的 File
对象,下面还有一个 raw
属性对象,功能实现使用 raw
对象
具体实现代码如下:
template
<el-form-item label="上传数据" prop="uploadData"> <el-upload class="upload-demo" ref="upload" drag :action="uploadAction" :data="ruleForm.uploadData" :on-change="onFileChange" :on-success="onUploadSuccess" :on-error="onUploadError" :auto-upload="false" :file-list="fileList" > <i class="el-icon-upload"></i> <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> <div class="el-upload__tip line-height-none" slot="tip">只能上传csv文件,且最大不能超过10G</div> </el-upload> </el-form-item>
script
import SparkMD5 from 'spark-md5' ... methods: { onFileChange (file, fileList) { // element 中组件对 file 进行加工,这里使用未加工的对象,只有未加工的对象才能在 blobSlice.call() 中正常操作 let fileRaw = file.raw this.fileName = file.name let arr = file.name.split('.') if (arr && arr.length > 1) { let suffixName = arr[arr.length - 1].toLowerCase() if (suffixName !== 'csv') { this.$message.error('文件类型错误,请重新上传!') let index = this.fileList - 1 this.fileList.splice(index, 1) return false } } let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice let fileReader = new FileReader() let chunkSize = 2097152 let chunks = Math.ceil(file.size / chunkSize) let currentChunk = 0 let spark = new SparkMD5() fileReader.onload = function (e) { spark.append(e.target.result) currentChunk++ if (currentChunk < chunks) { loadNext() } else { console.log('computed hash', spark.end()) } } fileReader.onerror = function () { console.warn('FileReader error.') } function loadNext () { let start = currentChunk * chunkSize let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize // 注意这里的 fileRaw fileReader.readAsArrayBuffer(blobSlice.call(fileRaw, start, end)) } loadNext() } ...
关于 spark.end()
使用时要注意一下, 只能使用一次. 第一次调用后再次调用 spark.end()
后面的值都是有问题的。
如使用 console.log('computed hash', spark.end())
语句执行结束后,再次执行 let result = spark.end()
获取值这时候得到的 value
是不对的, 总是会得到这样一个值 d41d8cd98f00b204e9800998ecf8427e