文件上传看似很常规的需求,要做极致真不简单,以下这些点你都做到了吗?
-
上传进度条(交互优化)
-
同一个文件多次上传不起效
-
传错文件了,图片不满意重新上传
-
文件大小校验
- fileObj.size校验
- 文件二进制头信息校验
-
校验类型(扩展名)
- fileObj.name 做校验不靠谱(修改的扩展名如何校验)
- 文件二进制头信息校验
-
校验宽高
-
是否支持拖拽上传
-
是否支持复制粘贴上传
-
是否支持大文件断网续传
-
大文件切片上传添加指纹
-
大文件MD5怎么优化
-
考虑性能和优化(web-worker/time-slice)
-
文件压缩(如 canvas压缩)
-
上传超时重试
-
。。。
基本满足上传刚性需求
<div class="file-upload-wrap">
<div class="file-item-wrap">
<img class="icon-camara" src="../assets/img/camara.svg" >
<img
v-show="isShow"
class="icon-close"
@click="deletePreview"
src="../assets/img/close.svg" >
<p class="des">请选择身份证正面照片或者拍照上传</p>
<!-- 上传预览 -->
<img class="preview"
v-show="isShow"
:src="imgSrc" >
<input
class="inp"
type="file"
ref="inp"
@click="clearInp"
@change="showPreview">
</div>
<button @click="upload">上传</button>
</div>
data() {
return {
imgSrc: '',
fileObj: {}
}
},
computed: {
isShow() {
return !!this.imgSrc
}
},
methods: {
//本地预览
showPreview() {
const vm = this
this.fileObj = vm.$refs.inp.files[0]
if (!window.FileReader) return
const reader = new FileReader()
reader.readAsDataURL(vm.fileObj)
reader.onloadend = function() {
// 此时this 指向fileRender
const that = this
vm.$nextTick(() => {
vm.imgSrc = that.result
})
}
},
upload() {
// 校验文件格式
this.checkType(this.fileObj)
// 校验大小
this.checkSize(this.fileObj)
// 上传
this.uplodHandler(this.fileObj)
},
// 转换图片为二进制类型
dataURLtoBlob(dataurl) {
const vm = this
vm.hexStr = ''
let dataViewObj = {}
if (!window.FileReader) return
const reader = new FileReader()
reader.readAsArrayBuffer(vm.fileObj)
reader.onloadend = function(res) {
const that = this
vm.$nextTick(() => {
dataViewObj = new DataView(that.result)
for (let i = 0; i < dataViewObj.byteLength; i++) {
vm.hexStr += dataViewObj.getUint8(i).toString(16)
}
// 校验文件格式
vm.checkType(vm.hexStr)
})
}
},
// 校验图片类型
checkType(hexStr) {
const mimeTypeList = [{ typeName: 'jpg/jpeg', hexStr: 'FFD8FF' }, { typeName: 'png', hexStr: '89504E47' }]
// 依据二进制文件头信息来做判断,防止修改扩展名
const jpegHex = hexStr.slice(0, 6).toUpperCase()
const pngHex = hexStr.slice(0, 8).toUpperCase()
const matchTypeList = mimeTypeList.filter((item) => item.hexStr === jpegHex || item.hexStr === pngHex)
if (!matchTypeList.length) {
this.$dialog.alert({
title: '温馨提示',
message: '请上传格式为png,jpg 或jpeg的照片格式'
})
return false
}
/* // 获取文件name
const name = fileObj.name
// 文件扩展名
const ext = name.split('.')[1].toLowerCase()
// 允许上传的格式
const imgType = ['png', 'jpg', 'jpeg', 'PNG', 'JPG', 'JPEG']
const filterType = imgType.filter((item) => item === ext)
if (!filterType.length) {
this.$dialog.alert({
title: '温馨提示',
message: '请上传格式为png,jpg 或jpeg的照片格式'
})
return false
} */
},
// 校验大小
checkSize(fileObj) {
if (fileObj.size >= 10 * 1024) {
this.$dialog.alert({
title: '温馨提示',
message: '照片大小不能超过10k'
})
return false
}
},
// 上传
uplodHandler(fileObj) {
// TODO 上传接口
axios.post(...)
},
// 清除input值
clearInp() {
this.$refs.inp.value = ''
},
// 删除重新上传
deletePreview() {
this.imgSrc = ''
this.$refs.inp.value = ''
}
持续更新中。。。
// TODO
添加进度条
拖拽上传
复制粘贴上传
文件转二进制进行头信息校验(防止篡改文件扩展名)
- 文件类型头信息标识
#文件头信息
JPEG (jpg),文件头:FFD8FF
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638
TIFF (tif),文件头:49492A00
Windows Bitmap (bmp),文件头:424D
CAD (dwg),文件头:41433130
Adobe Photoshop (psd),文件头:38425053
Rich Text Format (rtf),文件头:7B5C727466
XML (xml),文件头:3C3F786D6C
HTML (html),文件头:68746D6C3E
Email [thorough only] (eml),文件头:44656C69766572792D646174653A
Outlook Express (dbx),文件头:CFAD12FEC5FD746F
Outlook (pst),文件头:2142444E
MS Word/Excel (xls.or.doc),文件头:D0CF11E0
MS Access (mdb),文件头:5374616E64617264204A
WordPerfect (wpd),文件头:FF575043
Postscript (eps.or.ps),文件头:252150532D41646F6265
Adobe Acrobat (pdf),文件头:255044462D312E
Quicken (qdf),文件头:AC9EBD8F
Windows Password (pwl),文件头:E3828596
ZIP Archive (zip),文件头:504B0304
RAR Archive (rar),文件头:52617221
Wave (wav),文件头:57415645
AVI (avi),文件头:41564920
Real Audio (ram),文件头:2E7261FD
Real Media (rm),文件头:2E524D46
MPEG (mpg),文件头:000001BA
MPEG (mpg),文件头:000001B3
Quicktime (mov),文件头:6D6F6F76
Windows Media (asf),文件头:3026B2758E66CF11
MIDI (mid),文件头:4D546864
- 文件转化
/**
* 网络图像文件转Base64
*/
function getBase64Image(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
var dataURL = canvas.toDataURL("image/" + ext);
return dataURL;
}
/**
*Base64字符串转二进制
*/
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});