js选择文件上传
之前传统的选择文件方式是给一个input标签点击上传,然后美化一下UI。后面出了一些新的API,可以用js方式来处理选择文件这个操作。
以下是一些相关的API:
Window.showSaveFilePicker()
Window.showOpenFilePicker()
Window.showDirectoryPicker()
Window.showModalDialog()(已删除)
使用的时候需要查询一下兼容性,比如在pc浏览器模拟器里可以上传,但是在移动端浏览器上却不会出现选择文件的弹窗,需要写一个polyfill,对于不能调用showOpenFilePickerPolyfill方法的用传统方式
async showOpenFilePickerPolyfill (options = [{}]) {
if (!Array.isArray(options)) {
options = [options]
}
return new Promise((resolve, reject) => {
const input = document.createElement('input')
input.type = 'file'
const accept = [
...options.map((option) => option.mimeTypes || []),
...options.map((option) => option.extensions || [])
].join()
input.multiple = options[0].multiple || false
input.accept = accept || ''
input.style.display = 'none'
document.body.append(input)
const _reject = () => cleanupListenersAndMaybeReject(reject)
const _resolve = (value) => {
if (typeof cleanupListenersAndMaybeReject === 'function') {
cleanupListenersAndMaybeReject()
}
resolve(value)
}
const cleanupListenersAndMaybeReject =
options[0].legacySetup &&
options[0].legacySetup(_resolve, _reject, input)
const cancelDetector = () => {
window.removeEventListener('focus', cancelDetector)
input.remove()
}
input.addEventListener('click', () => {
window.addEventListener('focus', cancelDetector)
})
input.addEventListener('change', () => {
window.removeEventListener('focus', cancelDetector)
input.remove()
_resolve(input.multiple ? Array.from(input.files) : input.files[0])
})
if ('showPicker' in HTMLInputElement.prototype) {
input.showPicker()
} else {
input.click()
}
})
}
supported () {
if (typeof window.self === 'undefined') {
return false
}
if ('top' in window.self && window.self !== window.top) {
try {
// eslint-disable-next-line no-unused-expressions
window.top.location + ''
} catch (e) {
return false
}
return false
} else if ('showOpenFilePicker' in window.self) {
return 'showOpenFilePicker'
}
return false
}
其他API polyfill请参考:
https://github.com/GoogleChromeLabs/browser-fs-access
裁剪工具
可能通过搜索出现最多的是vue-copper,经过一通操作后,发现问题很多,并且卡顿,后面更换了vue-advanced-cropper,感觉还挺不错的,可以满足要求,移动顺畅。这后面的操作就比较简单了,就是通过使用这个插件,获取相应的blob或者base64来上传到服务器
async handleChangeAvatar () {
var src = ''
if (this.supported()) {
const arrFileHandle = await window.showOpenFilePicker(this.options)
src = await this.handleFiletoBlob(arrFileHandle)
} else {
const blob = await this.showOpenFilePickerPolyfill(this.options)
src = URL.createObjectURL(blob)
}
this.option.img = src
this.showCopper = true
this.$nextTick(() => {
const ele = document.querySelector('#app')
ele.appendChild(this.$refs.showCopper)
})
this.showAvatar = false
this.showSheet = false
}
获取图像
const { canvas } = this.$refs.cropper.getResult()