我们或多或少都使用过各式各样的富文本编辑器,其中有一个很方便功能,复制一张图片然后粘贴进文本框,这张图片就被上传了,那么这个方便的功能是如何实现的呢?

原理分析
提取操作:复制=>粘贴=>上传
在这个操作过程中,我们需要做的就是:监听粘贴事件=>获取剪贴板里的内容=>发请求上传
为方便理解下文,需要先明白几点:

  1. 我们只能上传网页图(在网页上右键图片,然后复制)和截图(截图工具截的图片,eg:qq截图),不能粘贴上传系统里的图片(从桌面上、硬盘里复制),他们是存在完全不同的地方的。
  2. 截图工具截的图与在网页点击右键复制的图是有些不同的,因此处理方式也不一样。
  3. 知悉paste
    event这个事件:当进行粘贴(右键paste/ctrl+v)操作时,该动作将触发名为'paste'的剪贴板事件,这个事件的触发是在剪贴板里的数据插入到目标元素之前。如果目标元素(光标所在位置)是可编辑的元素(eg:设置了contenteditable属性的div。textarea并不行。),粘贴动作将把剪贴板里的数据,以最合适的格式,插入到目标元素里;如果目标元素不可编辑,则不会插入数据,但依然触发paste
    event。数据在粘贴的过程中是只读的。此段是翻译于w3.org_the-paste-action。
  4. 可惜的是,经过试验,发现chrome(当前最新版)、firefox(当前最新版)、ie11对paste事件的实现并不是完全按照w3c来的,各自也有区别(w3c的paste标准也因此只是草案阶段)。

test代码及截图如下:

  chrome:

  1. <textarea ></textarea>
  2. <div contenteditable style="width: 100px;height: 100px; border:1px solid">
  3. </div>
  4. <script>
  5. document.addEventListener('paste', function (event) {
  6. console.log(event)
  7. })
  8. </script>

            

  1. event有clipboardData属性,且clipboardData有item属性,clipboardData.item中的元素(对象)有type和kind属性;
  2. 无论在哪进行粘贴,均可触发paste事件;
  3. 在div(未特殊声明时,本文div均指设置了contenteditable属性的div) 里粘贴截图,不显示图片。img.src为base64编码字符串;
  4. 在div里粘贴网页图片,直接显示图片,img.src为图片地址。

 

firefox:

  

1. event有clipboardData属性,clipboardData没有item属性;

2. 只有在textarea里或者可编辑的div(里才粘贴才触发paste事件;

3. 在div里粘贴截图,直接显示图片,img.src为base64编码字符串;

4. 在div里粘贴网页图片,表现同chrome。

ie11:(不截图了,可自行试验,其他浏览器同理<( ̄▽ ̄)/,因为懒...)

  1. event没有clipboardData属性;
  2. 只在可编辑的div中粘贴才触发paste事件;
  3. 在div里粘贴截图,直接显示图片,img.src为base64编码字符串;
  4. 在div里粘贴网页图片,表现同chrome。

监听了paste事件,也知道了表现形式,接下来就是如何获取数据了:
chrome有特定的方法,利用clipboardData.items、getAsFile()、new
FileReader()等api可以在paste回调函数里获取到剪贴板里图片的base64编码字符串(无论是截图粘贴的还是网页图片复制粘贴的),ie11,firefox没有这样的api,不过依然有办法可以获取,因为数据已经表现在img的src里了,对于截图粘贴的,直接取img的src属性值(base64),对于网页粘贴的,则把地址传给后台,然后根据地址down下来,存在自己的服务器,最后把新地址返回来交给前端展示就ok了。为了保持一致性便于管理,统一将所有情况(截图、网页)中的img的src属性替换为自己存储的地址。因此可以得到以下核心代码(注释很全哦~~):

html展示:

  1. <head>
  2. <meta charset="UTF-8">
  3. <title>Document</title>
  4. <style>
  5. body {
  6. display: -webkit-flex;
  7. display: flex;
  8. -webkit-justify-content: center;
  9. justify-content: center;
  10. }
  11. #tar_box {
  12. width: 500px;
  13. height: 500px;
  14. border: 1px solid red;
  15. }
  16. </style>

前端js处理逻辑:

  1. document.addEventListener('paste', function (event) {
  2. console.log(event)
  3. var isChrome = false;
  4. if ( event.clipboardData || event.originalEvent ) {
  5. //not for ie11 某些chrome版本使用的是event.originalEvent
  6. var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
  7. if ( clipboardData.items ) {
  8. // for chrome
  9. var items = clipboardData.items,
  10. len = items.length,
  11. blob = null;
  12. isChrome = true;
  13. //items.length比较有意思,初步判断是根据mime类型来的,即有几种mime类型,长度就是几(待验证)
  14. //如果粘贴纯文本,那么len=1,如果粘贴网页图片,len=2, items[0].type = 'text/plain', items[1].type = 'image/*'
  15. //如果使用截图工具粘贴图片,len=1, items[0].type = 'image/png'
  16. //如果粘贴纯文本+HTML,len=2, items[0].type = 'text/plain', items[1].type = 'text/html'
  17. // console.log('len:' + len);
  18. // console.log(items[0]);
  19. // console.log(items[1]);
  20. // console.log( 'items[0] kind:', items[0].kind );
  21. // console.log( 'items[0] MIME type:', items[0].type );
  22. // console.log( 'items[1] kind:', items[1].kind );
  23. // console.log( 'items[1] MIME type:', items[1].type );
  24.  
  25. //阻止默认行为即不让剪贴板内容在div中显示出来
  26. event.preventDefault();
  27.  
  28. //在items里找粘贴的image,据上面分析,需要循环
  29. for (var i = 0; i < len; i++) {
  30. if (items[i].type.indexOf("image") !== -1) {
  31. // console.log(items[i]);
  32. // console.log( typeof (items[i]));
  33.  
  34. //getAsFile() 此方法只是living standard firefox ie11 并不支持
  35. blob = items[i].getAsFile();
  36. }
  37. }
  38. if ( blob !== null ) {
  39. var reader = new FileReader();
  40. reader.onload = function (event) {
  41. // event.target.result 即为图片的Base64编码字符串
  42. var base64_str = event.target.result
  43. //可以在这里写上传逻辑 直接将base64编码的字符串上传(可以尝试传入blob对象,看看后台程序能否解析)
  44. uploadImgFromPaste(base64_str, 'paste', isChrome);
  45. }
  46. reader.readAsDataURL(blob);
  47. }
  48. } else {
  49. //for firefox
  50. setTimeout(function () {
  51. //设置setTimeout的原因是为了保证图片先插入到div里,然后去获取值
  52. var imgList = document.querySelectorAll('#tar_box img'),
  53. len = imgList.length,
  54. src_str = '',
  55. i;
  56. for ( i = 0; i < len; i ++ ) {
  57. if ( imgList[i].className !== 'my_img' ) {
  58. //如果是截图那么src_str就是base64 如果是复制的其他网页图片那么src_str就是此图片在别人服务器的地址
  59. src_str = imgList[i].src;
  60. }
  61. }
  62. uploadImgFromPaste(src_str, 'paste', isChrome);
  63. }, 1);
  64. }
  65. } else {
  66. //for ie11
  67. setTimeout(function () {
  68. var imgList = document.querySelectorAll('#tar_box img'),
  69. len = imgList.length,
  70. src_str = '',
  71. i;
  72. for ( i = 0; i < len; i ++ ) {
  73. if ( imgList[i].className !== 'my_img' ) {
  74. src_str = imgList[i].src;
  75. }
  76. }
  77. uploadImgFromPaste(src_str, 'paste', isChrome);
  78. }, 1);
  79. }
  80. })
  81.  
  82. function uploadImgFromPaste (file, type, isChrome) {
  83. var formData = new FormData();
  84. formData.append('image', file);
  85. formData.append('submission-type', type);
  86.  
  87. var xhr = new XMLHttpRequest();
  88. xhr.open('POST', '/upload_image_by_paste');
  89. xhr.onload = function () {
  90. if ( xhr.readyState === 4 ) {
  91. if ( xhr.status === 200 ) {
  92. var data = JSON.parse( xhr.responseText ),
  93. tarBox = document.getElementById('tar_box');
  94. if ( isChrome ) {
  95. var img = document.createElement('img');
  96. img.className = 'my_img';
  97. img.src = data.store_path;
  98. tarBox.appendChild(img);
  99. } else {
  100. var imgList = document.querySelectorAll('#tar_box img'),
  101. len = imgList.length,
  102. i;
  103. for ( i = 0; i < len; i ++) {
  104. if ( imgList[i].className !== 'my_img' ) {
  105. imgList[i].className = 'my_img';
  106. imgList[i].src = data.store_path;
  107. }
  108. }
  109. }
  110.  
  111. } else {
  112. console.log( xhr.statusText );
  113. }
  114. };
  115. };
  116. xhr.onerror = function (e) {
  117. console.log( xhr.statusText );
  118. }
  119. xhr.send(formData);
  120. }

 用express.js搭的简易后台的接收逻辑:

  1. router.post('/', upload.array(), function (req, res, next) {
  2. //1.获取客户端传来的src_str字符串=>判断是base64还是普通地址=>获取图片类型后缀(jpg/png etc)
  3. //=>如果是base64替换掉"前缀"("data:image\/png;base64," etc)
  4. //2.base64 转为 buffer对象 普通地址则先down下来
  5. //3.写入硬盘(后续可以将地址存入数据库)
  6. //4.返回picture地址
  7. var src_str = req.body.image,
  8. timestamp = new Date().getTime();
  9. if ( src_str.match(/^data:image\/png;base64,|^data:image\/jpg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,/) ) {
  10. //处理截图 src_str为base64字符串
  11. var pic_suffix = src_str.split(';',1)[0].split('/',2)[1],
  12. base64 = src_str.replace(/^data:image\/png;base64,|^data:image\/jpg;base64,|^data:image\/jpg;base64,|^data:image\/bmp;base64,/, ''),
  13. buf = new Buffer(base64, 'base64'),
  14. store_path = 'public/images/test_' + timestamp + '.' + pic_suffix;
  15.  
  16. fs.writeFile(store_path, buf, function (err) {
  17. if (err) {
  18. throw err;
  19. } else {
  20. res.json({'store_path': store_path});
  21. }
  22. });
  23. } else {// 处理非chrome的网页图片 src_str为图片地址
  24. var temp_array = src_str.split('.'),
  25. pic_suffix = temp_array[temp_array.length - 1],
  26. store_path = 'public/images/test_' + timestamp + '.' + pic_suffix,
  27. wstream = fs.createWriteStream(store_path);
  28.  
  29. request(src_str).pipe(wstream);
  30. wstream.on('finish', function (err) {
  31. if( err ) {
  32. throw err;
  33. } else {
  34. res.json({"store_path": store_path});
  35. }
  36. });
  37. }
  38. });

需要node环境:安装node=>npm intall=>node app.js)

以上就是本文的全部内容,希望对大家的学习有所帮助。

js实现ctrl+v粘贴上传图片(兼容chrome、firefox、ie11)【转载】的更多相关文章

  1. js实现ctrl+v粘贴上传图片(兼容chrome,firefox,ie11)

    背景 我们或多或少都使用过各式各样的富文本编辑器,其中有一个很方便功能,复制一张图片然后粘贴进文本框,这张图片就被上传了,那么这个方便的功能是如何实现的呢? 原理分析 提取操作:复制=>粘贴=& ...

  2. js实现ctrl+v粘贴图片或是截图

    浏览器环境:谷歌浏览器 1.ctrl+v粘贴图片都是监听paste时间实现的,复制的数据都存在clipboardData下面,虽然打印显示数据长度为0,但是还是可以获取数据的 2.打印clipboar ...

  3. js实现ctrl+v粘贴并上传图片

    前端页面: <textarea class="scroll" id="text" placeholder="在此输入...">& ...

  4. wangEditor实现ctrl+v粘贴word图片并上传

    很多时候我们用一些管理系统的时候,发布新闻.公告等文字类信息时,希望能很快的将word里面的内容直接粘贴到富文本编辑器里面,然后发布出来.减少排版复杂的工作量. 下面是借用百度doc 来快速实现这个w ...

  5. c# TextBox只允许输入数字,禁用右键粘贴,允许Ctrl+v粘贴数字

    TextBox只允许输入数字,最大长度为10 //TextBox.ShortcutsEnabled为false 禁止右键和Ctrl+v private void txtNumber_KeyPress( ...

  6. 2019-3-22c# TextBox只允许输入数字,禁用右键粘贴,允许Ctrl+v粘贴数字

    TextBox 禁止复制粘贴 ShortcutsEnabled =false TextBox只允许输入数字,最大长度为10 //TextBox.ShortcutsEnabled为false 禁止右键和 ...

  7. js实现ctrl+v上传图片

    描述:实现类似QQ截图删上传图片的功能 a.需要的js插件 paste.image.js 地址:https://github.com/iyangyuan/pasteimg b.paste.image. ...

  8. ueditor实现ctrl+v粘贴word图片并上传

    图片的复制无非有两种方法,一种是图片直接上传到服务器,另外一种转换成二进制流的base64码 目前限chrome浏览器使用,但是项目要求需要支持所有的浏览器,包括Windows和macOS系统.没有办 ...

  9. C# 控制台程序实现 Ctrl + V 粘贴功能

    代码主要分为两部分,首先调用系统API注册剪切板相关的事件,然后监控用户的按键操作.完整代码如下: class ClipBoard { [DllImport("user32.dll" ...

随机推荐

  1. Git及GitLab使用手册

    一.GitBash安装与使用 参考: https://www.cnblogs.com/jasonxu19900827/p/7823089.html 二.SourceTree安装与使用 SourceTr ...

  2. Java 问题集

    1.命令行编译.java文件,找不到或者无法加载主类,需要配置完整的PATH,CLASSPATH环境变量,CLASSPATH最前面是 点+分号 PATH=%JAVA_HOME%\binCLASSPAT ...

  3. WordPress版微信小程序2.0版本发布

    利用业余时间对WordPress版微信小程序进行的升级,增加了一些功能,程序性能上做了一些优化.经过此次的版本升级,WordPress版微信小程序所需的基本功能已经具备. 开放源码地址:https:/ ...

  4. 释放linux的buff/cache

    有个linux的服务器,2G内存的,今天登上去一看,内存竟然被占得满满的. ssh上去执行了free. free -m total used free shared buff/cache availa ...

  5. Python开发之AJAX

    一.概述 对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上. 1.传统的Web应用:一个简单操作 ...

  6. 蓝桥杯Java真题解析

    上个月参加蓝桥杯省赛拿了个省一,自从比赛完之后就一直没怎么写代码了,还有一个多月就要国赛了,从现在开始准备下国赛,但是我也不想学什么算法,而且我还在准备考研,所以就打算只做下历年的真题,争取国赛拿个国 ...

  7. Basler和Matrox的配置及调试

    说明: 本系列博文是我自己研究生课题,采用做一步记录一步,在论文答辩结束或者机器设计结束之后才会附上源代码! 自从装好相机和设计好机械结构之后就没有继续进行下一步,这段时间花了三四天继续上次任务进行, ...

  8. tensorflowwindows安装

    1.在python官网下载3.5 64位版本的python,并进行安装 https://www.python.org/ 2.进行安装,配置环境变量 3.命令行执行pip3 install numpy ...

  9. error C2011: “timespec”:“struct”类型重定义

    error C2011: “timespec”:“struct”类型重定义 C++ pthread pthread.h 中的 timespec 和time.h 中的 结构定义重复了 ,同时两个头文件中 ...

  10. SikuliI:安装过程(Windows)

    [转载至:http://blog.csdn.net/defectfinder/article/details/49819215] 一.简单介绍 SikuliIDE和Sikuli Script就是现在的 ...