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