最近在做一个论坛的项目,发布评论的时候,很多时候会用到截图上传的功能,通过微信截图,QQ截图,直接将截取的图片通过Ctrl+v
复制到输入框里,自动上传将图片渲染到页面上,今天就来实现一个这样的功能。
主要的知识点是,浏览的paste
事件,clipboardData
。
paste
一个标准的Dom事件,粘贴事件,会在用户按下Ctrl+v
,或者通过鼠标复制时触发.像其他事件一样,我们可以通过addEventListener
为一个Element
添加一个粘贴事件的监听函数 如以下代码。
document.addEventListener('paste', (event) => {
console.log('粘贴事件', event)
});
复制代码
Copy
第一步,实现图片粘贴到输入框
我使用vue,给多行输入框绑定一个v-paster的自定义指令,这个自定义指令直接监听元素的粘贴事件。
复制代码
Copy
// 指令粘贴指令定义
directives: {
paste: {
bind(el, binding, vnode) {
el.addEventListener('paste', function (event) {
//这里直接监听元素的粘贴事件
binding.value(event);
});
},
},
},
复制代码
Copy
第二步,通过监听paste事件获取文件数据,实现粘贴即上传
paste
一个标准的Dom事件,粘贴事件,会在用户按下Ctrl+v
,或者通过鼠标复制时触发.像其他事件一样,在下面的代码中我打印的是一个叫做ClipboardEvent
的对象,内部存在着一个叫做clipboardData
的属性,这便是我们在复制时存储数据的对象。 其中的items
就是我们要操作的对象,需要粘贴的元素都在其中存储。
然后调用FileReader对象,该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8。这个方法非常容易理解,将文件以文本方式读取,读取的结果即是这个文本文件中的内容。
**readAsBinaryString:**该方法将文件读取为二进制字符串,通常我们将它传送到后端,后端可以通过这段字符串存储文件。
**readAsDataURL:**这是例子程序中用到的方法,该方法将文件读取为一段以 data: 开头的字符串,这段字符串的实质就是 Data URL,Data URL是一种将小文件直接嵌入文档的方案。这里的小文件通常是指图像与 html 等格式的文件。
最后组装为一个formData对象上传
//微信截图上传图片时触发
handleParse(e) {
let file = null;
var data=e.clipboardData||window.clipboardData, that = this;
console.log('fun',data);
blob=data.items[0].getAsFile();
var isImg=(blob&&1)||-1;
var reader=new FileReader();
if(isImg>=0){
//将文件读取为 DataURL
reader.readAsDataURL(blob);
}
reader.onload=function(event){
//获取base64流
var base64_str=event.target.result;
//div中的img标签src属性赋值,可以直接展示图片
// console.log('base64_str',base64_str);
var bytes = window.atob(base64_str.split(',')[1]);
var array = [];
for(var i = 0; i < bytes.length; i++){
array.push(bytes.charCodeAt(i));
}
var blob = new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
var formData = new FormData();
formData.append('file',blob, Date.now() + '.jpg');
formData.append('filename', 'file')
console.log(blob);
that.update(formData);
}
},
update(formData) {
// 上传
fetch('http://xxx/xxx',{
method: 'post',
headers: {
authorization: localStorage.getItem('token')
},
body: formData
}).then(response => response.json()).then(res=>{
let pic = res.data.url;
})
},
复制代码
Copy
关于fetch上传图片,在上面的示例中其实失败了很久,最后找到的原因是请求头的content-type,我填了application/json和MulitPart/form-data 均不能成功上传,最后去掉这个content-type才上传成功。原因是fetch 作为浏览器自身提供的api,当传入的参数为 formDate 格式时,不可手动设置content-type。