在用户执行粘贴操做的时候,
js
可以得到剪切板的内容,本文讨论一下这个问题。
目前只有Chrome
支持获取剪切板中的图片数据。还好须要这个功能的产品目前只支持Chrome
和Safari
,一些Chrome
的新特性是能够尽情使用了,仍是可以覆盖到大部分用户的。因此本文只讨论Chrome
如何使用和如何阻止Safari
,原理大概了解了,再研究其余浏览器相关的问题就容易多了。javascript
paste
事件能够用js
给页面中的元素绑定paste
事件的方法,当用户鼠标在该元素上或者该元素处于focus
状态,绑定到paste
事件的方法就运行了。html
绑定的元素不必定是input
,普通的div
也是能够绑定的,若是是给document
绑定了,就至关于全局了,任什么时候候的粘贴操做都会触发。java
先写一下事件绑定的代码浏览器
pasteEle.addEventListener("paste", function (e){ if ( !(e.clipboardData && e.clipboardData.items) ) { return; } });
粘贴事件提供了一个clipboardData
的属性,若是该属性有items
属性,那么就能够查看items
中是否有图片类型的数据了。Chrome
有该属性,Safari
没有。函数
clipboardData
介绍介绍一下clipboardData
对象,它其实是一个DataTransfer
类型的对象,DataTransfer
是拖动产生的一个对象,但实际上粘贴事件也是它。测试
clipboardData
的属性介绍spa
属性 | 类型 | 说明 |
---|---|---|
dropEffect | String | 默认是 none |
effectAllowed | String | 默认是 uninitialized |
files | FileList | 粘贴操做为空List |
items | DataTransferItemList | 剪切板中的各项数据 |
types | Array | 剪切板中的数据类型 该属性在Safari下比较混乱 |
items
介绍items
是一个DataTransferItemList
对象,天然里面都是DataTransferItem
类型的数据了。操作系统
items
的DataTransferItem
有两个属性kind
和type
code
属性 | 说明 |
---|---|
kind | 通常为string 或者file |
type | 具体的数据类型,例如具体是哪一种类型字符串或者哪一种类型的文件,即MIME-Type |
方法 | 参数 | 说明 |
---|---|---|
getAsFile | 空 | 若是kind 是file ,能够用该方法获取到文件 |
getAsString | 回调函数 | 若是kind 是string ,能够用该方法获取到字符串,字符串须要用回调函数获得,回调函数的第一个参数就是剪切板中的字符串 |
在原型上还有一些其余方法,不过在处理剪切板操做的时候通常用不到了。htm
types
介绍通常types
中常见的值有text/plain
、text/html
、Files
。
值 | 说明 |
---|---|
text/plain | 普通字符串 |
text/html | 带有样式的html |
Files | 文件(例如剪切板中的数据) |
pasteEle.addEventListener("paste", function (e){ if ( !(e.clipboardData && e.clipboardData.items) ) { return ; } for (var i = 0, len = e.clipboardData.items.length; i < len; i++) { var item = e.clipboardData.items[i]; if (item.kind === "string") { item.getAsString(function (str) { // str 是获取到的字符串 }) } else if (item.kind === "file") { var pasteFile = item.getAsFile(); // pasteFile就是获取到的文件 } } });
注意若是是string
类型的数据,可能针对具体是text/plain
、text/html
进行分别的处理。
一切看似都很顺利,若是用户粘贴了图片,经过上面的方法咱们是能够获取到,能够对图片进行上传等操做了。
首先要说一下js经过剪切板能获取到的图片是怎么来的,它必须是用QQ截图或者系统截图功能截下来的图片,或者是网页上某个图片单击右键复制图片等。
可是若是用户复制Mac
的Finder
中的一个图片文件,实际上js是没有办法获取到这个图片的。可是js确实会得到一个图片类型的文件,这个图片实际上图片在电脑中的图标标识,说的比较抽象,直接上图。
若是复制的是JPEG
图片,粘贴过来的倒是Mac
上的文件缩略图,后面依次是PNG
、GIF
、ZIP
、DMG
、Mac目录
的文件缩略图。
很明显,这不是咱们期待获得的粘贴的结果,咱们期待获得文件,但实际上却获得该文件在操做系统上的缩略图。
不过粘贴事件带来的数据还有一个字符串,就是该文件的名字,因此能够用下面的方法Hack掉。
var cbd = e.clipboardData; if(cbd.items && cbd.items.length === 2 && cbd.items[0].kind === "string" && cbd.items[1].kind === "file" && cbd.types && cbd.types.length === 2 && cbd.types[0] === "text/plain" && cbd.types[1] === "Files"){ return; }
这么多的判断条件,基本能够肯定经过剪切板过来的是粘贴的文件。我刚才测试了Windows
的Chrome
,不会有这个问题,固然也不能经过复制文件的方法获得任何文件。
当我打算写这篇博客的时候,Chrome
开发版已经升级到了49,上面的Bug忽然消失了,囧。
因此上面的Hack应该加上版本限制了。
var ua = window.navigator.userAgent; ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49
应该在上面的Hack再加上这两个判断,便是Mac
下的Chrome
49版本如下就要return
。
因为公司IM系统正在迁移到V2消息系统,并且现有的文件类库没有办法知足业务需求,要本身封装一个文件上传库。
而后副总找到产品经理,说新版怎么不支持Excel
的粘贴,临时排期一天修复这个问题,当时是这样解决的,若是items
长度是1而且是文件类型(单纯粘贴一个文件),则上传,若是items
长度是4且第4个是文件类型(通过测试是Excel的粘贴结果),则上传。
当时担忧因为用户各类误操做,粘贴了不应粘贴的东西,文件上传错误,用了这种白名单机制去过滤,可是万一之后有比Excel
粘贴获得的数据更其余的类型,就须要单独写代码兼容,因此,如今改为了若是判断是有Bug的状况,直接return
,属于黑名单机制,这样之后再发现黑名单的状况,再添加。
// demo 程序将粘贴事件绑定到 document 上 document.addEventListener("paste", function (e) { var cbd = e.clipboardData; var ua = window.navigator.userAgent; // 若是是 Safari 直接 return if ( !(e.clipboardData && e.clipboardData.items) ) { return; } // Mac平台下Chrome49版本如下 复制Finder中的文件的Bug Hack掉 if(cbd.items && cbd.items.length === 2 && cbd.items[0].kind === "string" && cbd.items[1].kind === "file" && cbd.types && cbd.types.length === 2 && cbd.types[0] === "text/plain" && cbd.types[1] === "Files" && ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49){ return; } for(var i = 0; i < cbd.items.length; i++) { var item = cbd.items[i]; if(item.kind == "file"){ var blob = item.getAsFile(); if (blob.size === 0) { return; } // blob 就是从剪切板得到的文件 能够进行上传或其余操做 } } }, false);