js implements ctrl+v pasting and uploading pictures (compatible with chrome, firefox, ie11)

Keywords: Attribute encoding Firefox Front-end

Rich text editor, which has a very convenient function, copy a picture and paste it into the text box, the picture was uploaded, then how to achieve this convenient function?

Note: Pay more attention to the red font part!!!

Principle analysis
Extraction operation: copy => paste => upload
In this process, what we need to do is: listen for the paste event => get the content in the clipboard => send the request to upload.
In order to understand the following, we need to understand a few points:

  • We can only upload web pages (right-click pictures on the web page, then copy) and screenshots (screenshots from the tool, eg: qq q screenshots), but not upload pictures from the upload system (copy from the desktop, hard disk). They are completely different places.
  • The screenshots taken by the screenshots tool are somewhat different from those copied by right-clicking on a web page, so the processing is different.
  • Know about paste event: When paste (right-click paste/ctrl+v) is performed, the action triggers a clipboard event named'paste', which triggers the event before the data in the clipboard is inserted into the target element. If the target element (where the cursor is located) is an editable element (eg): the div of the contenteditable attribute is set. textarea doesn't work.) The paste action inserts the data from the clipboard into the target element in the most appropriate format; if the target element is not editable, it does not insert the data, but still triggers paste event. Data is read-only during pasting. This paragraph is translated at w3.org_the-paste-action.
  • Unfortunately, through experiments, we found that chrome (the latest version), Firefox (the latest version) and ie11 did not implement paste events entirely in accordance with w3c, and there are differences between them (the paste standard of W3C is therefore only the draft stage).
The test code and screenshots are as follows:

chrome:

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


Be careful:

  1. event has clipboard Data attribute, clipboard Data has item attribute, and elements (objects) in clipboard Data. item have type and kind attribute.
  2. paste events can be triggered wherever pastes are pasted.
  3. In div (without special declaration, div in this article refers to div with content ditable property set), paste screenshots, do not display pictures. img.src is a base64 encoding string.
  4. In div, paste the picture of the web page and display the picture directly. img.src is the address of the picture.
firefox:


  1. event has clipboard Data attribute, clipboard Data has no item attribute;
  2. paste events are triggered only in textarea or editable div s.
  3. Paste screenshots in div to display pictures directly, img.src is base64 encoding string;
  4. In div, paste a picture of the web page, which is the same as chrome.

ie11:  

  • Evet has no clipboard Data attribute;
  • paste events are triggered only by pasting in editable div s.
  • Paste screenshots in div to display pictures directly, img.src is base64 encoding string;
  • In div, paste a picture of the web page, which is the same as chrome.

After listening to the paste event and knowing the manifestation, the next step is how to get the data:
chrome has a specific method, using clipboard data. items, getAsFile(), new FileReader() and other apis, you can get the base64 encoding string of the picture in the clipboard in the paste callback function (whether it's clipboard pasted or web page copy-pasted). ie11, firefox do not have such an api, but there are still ways to do it. Get, because the data has been shown in the src of img, for the screenshot pasted, directly take the src attribute value of img (base64), for the web page pasted, the address is passed to the background, then according to the address down, there is its own server, and finally return the new address to the front end to show ok ay. In order to maintain consistency and facilitate management, the src attribute of img in all cases (screenshots, web pages) is replaced by the address stored by itself. So you can get the following core code:

html shows:

<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
body {
  display: -webkit-flex; 
  display: flex;    
  -webkit-justify-content: center;
  justify-content: center;
}
#tar_box {
  width: 500px;
  height: 500px;
  border: 1px solid red;
}
</style>

Front-end js processing logic:

document.addEventListener('paste', function (event) {
  console.log(event)
  var isChrome = false;
  if ( event.clipboardData || event.originalEvent ) {
    //Some versions of chrome not for ie11 use event.originalEvent
    var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
    if ( clipboardData.items ) {
      // for chrome
      var  items = clipboardData.items,
        len = items.length,
        blob = null;
      isChrome = true;
      //items.length is more interesting, preliminary judgment is based on the type of mime, that is, there are several types of mime, the length is several (to be verified)
      //If plain text is pasted, then len=1, if page picture is pasted, len = 2, items [0]. type = text / plain, items [1]. type = image /*'
      //If you paste a picture using the screenshot tool, len = 1, items [0]. type ='image / png'
      //If you paste plain text + HTML, len = 2, items [0], type = text / plain', items [1], type = text / html'
      // console.log('len:' + len);
      // console.log(items[0]);
      // console.log(items[1]);
      // console.log( 'items[0] kind:', items[0].kind );
      // console.log( 'items[0] MIME type:', items[0].type );
      // console.log( 'items[1] kind:', items[1].kind );
      // console.log( 'items[1] MIME type:', items[1].type );

      //Prevent default behavior by not allowing clipboard content to be displayed in div s
      event.preventDefault();

      //Look for the pasted image in items. According to the analysis above, you need to loop  
      for (var i = 0; i < len; i++) {
        if (items[i].type.indexOf("image") !== -1) {
          // console.log(items[i]);
          // console.log( typeof (items[i]));

          //getAsFile() This method is only supported by living standard firefox ie11.        
          blob = items[i].getAsFile();
        }
      }
      if ( blob !== null ) {
        var reader = new FileReader();
        reader.onload = function (event) {
          // event.target.result is the Base64 encoding string of the picture.
          var base64_str = event.target.result
          //You can write upload logic here to upload base64 encoding strings directly (you can try to pass in a blob object to see if the background program can parse)
          uploadImgFromPaste(base64_str, 'paste', isChrome);
        }
        reader.readAsDataURL(blob); 
      }
    } else {
      //for firefox
      setTimeout(function () {
        //The reason for setting Timeout is to ensure that the image is inserted into the div first and then the value is obtained.
        var imgList = document.querySelectorAll('#tar_box img'),
          len = imgList.length,
          src_str = '',
          i;
        for ( i = 0; i < len; i ++ ) {
          if ( imgList[i].className !== 'my_img' ) {
            //If it's a screenshot, src_str is base64. If it's a copy of another web page image, src_str is the address of the image on another server.
            src_str = imgList[i].src;
          }
        }
        uploadImgFromPaste(src_str, 'paste', isChrome);
      }, 1);
    }
  } else {
    //for ie11
    setTimeout(function () {
      var imgList = document.querySelectorAll('#tar_box img'),
        len = imgList.length,
        src_str = '',
        i;
      for ( i = 0; i < len; i ++ ) {
        if ( imgList[i].className !== 'my_img' ) {
          src_str = imgList[i].src;
        }
      }
      uploadImgFromPaste(src_str, 'paste', isChrome);
    }, 1);
  }
})

function uploadImgFromPaste (file, type, isChrome) {
  var formData = new FormData();
  formData.append('image', file);
  formData.append('submission-type', type);

  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/upload_image_by_paste');
  xhr.onload = function () {
    if ( xhr.readyState === 4 ) {
      if ( xhr.status === 200 ) {
        var data = JSON.parse( xhr.responseText ),
          tarBox = document.getElementById('tar_box');
        if ( isChrome ) {
          var img = document.createElement('img');
          img.className = 'my_img';
          img.src = data.store_path;
          tarBox.appendChild(img);
        } else {
          var imgList = document.querySelectorAll('#tar_box img'),
            len = imgList.length,
            i;
          for ( i = 0; i < len; i ++) {
            if ( imgList[i].className !== 'my_img' ) {
              imgList[i].className = 'my_img';
              imgList[i].src = data.store_path;
            }
          }
        }

      } else {
        console.log( xhr.statusText );
      }
    };
  };
  xhr.onerror = function (e) {
    console.log( xhr.statusText );
  }
  xhr.send(formData);
}
Here is only the front-end code, the back-end code please see the original http://www.jb51.net/article/80657.htm or self-written!

From: http://www.jb51.net/article/80657.htm

Posted by prudens on Mon, 10 Dec 2018 23:21:06 -0800