The front end automatically generates images and downloads them (less than 60 lines of code)

Preview the process

demand

There were some simple image collages that needed to be done (about 8,000), but there was no goofy software that could handle them well, and it was frustrating that nodeJS’s image libraries were pretty much useless and time-consuming to install (I installed them anyway). As an aspirational front end, wouldn’t it be nice to implement it directly on the front page without relying on anything else? And so came this little experiment. In fact, very simple, only to record in this article, afraid not to forget later.

A prelude to

1. Due to the security limitation of Canvas, we must ensure that resources and web pages are carried out under the same domain name, otherwise there is no problem with drawing, but the error of “canvas is polluted” will be reported when exporting. So we have a number of solutions:

  • Nodejs builds a local server with all resources in the same domain. Features: Suitable for deployment online use, but troublesome
  • Remove Chrome cross-domain restrictions locally (using Chrome launch parameters). Features: Simple and crude, local quick problem solving, but only suitable for your own use
  • Img tag addedimg.setAttribute('crossOrigin', 'anonymous'). Features: Can solve current problems, but has hidden dangers
  • Use the new FileReader (); Read the requested BLOB data as a Base64 address. Features: code added, no other shortcomings found
  • Use url.createObjecturl (res) to read the requested BLOB as a BLOB address. Features: Add code and remember itURL.revokeObjectURL Cancel the association, no other shortcomings found.

These are five solutions that I have personally explored, each tailored to the needs of different scenarios.

The first two examples are not easy to show, but here are the corresponding code examples for the three methods:

let can = document.getElementById('canvas');
let ctx = can.getContext('2d');
fetch('https://img.alicdn.com/bao/uploaded/i1/446338500/O1CN01npzdZ52Cf3A4cx8JG_!! 0-item_pic.jpg_240x240.jpg').then(
      res= > res.blob())
    .then(res= > {
      let fr = new FileReader();//https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
      fr.onload = function (e) {
        console.log(e)
        console.log(e.target.result)
        let img = new Image();
		// The first way
        // img.setAttribute('crossOrigin', 'anonymous');
        // img.src =
        // 'https://img.alicdn.com/bao/uploaded/i1/446338500/O1CN01npzdZ52Cf3A4cx8JG_!! 0-item_pic.jpg_240x240.jpg'
        // The second way
        // img.src = e.target.result; / / into base64
        // The third way
        // img.src = URL.createObjectURL(res); // Convert to Blob address ObjectUrl

        img.onload = function () {
          ctx.drawImage(img, 0.0)
          console.log(can.toDataURL())// Export to dataURL
          // canvas.toblob (function (blob) {// export toBlob address
          // var url = URL.createObjectURL(blob);
          // console.log(url)
          // });
        }
      }
      fr.onerror = function () {
        console.log('Read error! ')
      }
      fr.readAsDataURL(res)// The second argument can be encoded if it is converted to text
    })
Copy the code
The core

The whole point of the process here is to fill the canvas with content using the Base64 address or createObjectURL virtual address so that it’s not a polluted canvas.

Improve the export

With the previous prelude flow, we can start to implement the download, using the A tag download. Also note that the A tag Download attribute does not support cross-domain, but in our case, cross-domain is not involved (canvas data is generated from this page).

How to batch export?

Note here that since we have some asynchronous links, we want to implement sequential execution of them using Async await. In this case, it will be executed in order, and the browser should be enabled to allow automatic downloading (local environment may always prompt, but if you host a localhost, you will not have a problem).

Example:

  (async() = > {let result = []
    let arr = []
    for (let i = 0; i < arr.length; i++) {
      await new Promise((res, rej) = > {
        setTimeout(() = > {
          res()
        }, 10)})try {
        await createImg(arr[i]).catch(err= > {
          result.push(arr[i])
          console.log(arr[i], 'Server response error, unable to get image')})}catch (e) {
        result.push(arr[i])
        console.log(arr[i], 'The program cannot process the image'.'Reason:', e)
      }
    }

    document.body.innerText = result.join("")

  })()


  function createImg(article) {
    return new Promise((res, rej) = > {
      let temp = new Image()
      temp.src = 'http://XXXXXX/getpic? type=touming&article=' + article;
      temp.onerror = function () {
        rej(article)
      }
      temp.onload = function () {
        ctx.drawImage(temp, 940.90.650.650);
        var bloburl = canvas.toDataURL('image/jpeg');
        var anchor = document.createElement('a');
        anchor.href = bloburl;
        anchor.download = article;
        anchor.click()
        ctx.rect(0.0.2000.800);
        ctx.fillStyle = "RGB (236237239)";
        ctx.fill();
        res()
      }
    })
  }
Copy the code

Hit the pit:

Note here that I have defined the onError event of the Image object, because the async await Promise does not know that it caught the server error (if the server goes down for a while, the script will stop the error), so we need to introduce it in onError and reject it. Promise can get an error message and catch it later. To prevent complex error stops, a try catch is added to ensure that errors are retrieved.