前端自動生成圖片並下載(不到60行代碼)

前端自動生成圖片並下載(不到60行代碼)

預覽流程

輸入圖片說明

需求

因爲一些簡單的圖片拼合須要處理(大概8000張),可是又沒有找到合適的傻瓜軟件可以很好地解決需求,同時也很使人頭疼的是nodejs裏面圖片處理庫,基本上沒幾個好用,且安裝費時費勁 (固然我仍是裝了)。做爲一個有追求的前端,若是能不依靠其餘的東西,直接在前端頁面上實現它不香麼?因而就有了這個小嚐試。其實很是簡單,僅以此文記錄一下,怕之後不用忘記。前端

前奏

1.因爲canvas的安全限制,咱們必須保證資源和網頁在同一個域名下進行,不然畫入是沒問題的,可是導出就會報「畫布被污染」的錯誤。因此咱們一共有多個解決辦法:node

  • nodejs搭建一個本地服務器,全部資源都在同域下。特色:適合部署上線使用,但麻煩
  • 本地解除chrome跨域限制(利用chrome啓動參數)。特色:簡單粗暴,本地快速解決問題,但只適合本身使用
  • img標籤增長img.setAttribute('crossOrigin', 'anonymous')。特色:能解決目前的問題,可是有隱患
  • 使用new FileReader();將請求到的blob數據讀取成base64地址。特色:代碼增長,其餘沒發現缺點
  • 使用URL.createObjectURL(res) 將請求到的blob數據讀取成Blob類型的地址。特色:代碼增長,同時要記得URL.revokeObjectURL取消掉關聯,其餘沒發現缺點。

以上是我我的探索的五種解決辦法,分別適合不一樣場景下的需求。chrome

前兩種例子不便於展現,直接下面是三種辦法的對應代碼例子:canvas

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();
      fr.onload = function (e) {
        console.log(e)
        console.log(e.target.result)
        let img = new Image();
		//第一種辦法
        // img.setAttribute('crossOrigin', 'anonymous');
        // img.src =
        // 'https://img.alicdn.com/bao/uploaded/i1/446338500/O1CN01npzdZ52Cf3A4cx8JG_!!0-item_pic.jpg_240x240.jpg'
        //第二種辦法
        // img.src = e.target.result; //轉成base64
        //第三種辦法
        // img.src = URL.createObjectURL(res);//轉成Blob地址ObjectUrl

        img.onload = function () {
          ctx.drawImage(img, 0, 0)
          console.log(can.toDataURL())//導出成dataurl
          // canvas.toBlob(function (blob) {//導出成blob地址
          // var url = URL.createObjectURL(blob);
          // console.log(url)
          // });
        }
      }
      fr.onerror = function () {
        console.log('讀取錯誤!')
      }
      fr.readAsDataURL(res)
    })
複製代碼
完善導出

有了以前的前奏流程,接下來咱們能夠開始實現下載,利用a標籤的download便可。同時要注意,a標籤download屬性不支持跨域,不過在咱們這裏面,不會涉及到跨域(canvas數據就是從這頁面裏產生的)。跨域

如何批量導出?promise

在這裏要注意,因爲咱們存在一些異步環節,咱們要使用Async await來實現對它們的順序執行。這樣的話,就會按照順序執行,另外瀏覽器記得打開容許自動下載(本地環境可能會老有提示,可是若是拿一個localhost託管就不會有問題了)瀏覽器

例子:安全

(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], '服務器端響應出錯,沒法獲取圖片')
        })
      } catch (e) {
        result.push(arr[i])
        console.log(arr[i], '程序處理時沒法處理圖片', '緣由:', 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(236,237,239)";
        ctx.fill();
        res()
      }
    })
  }
複製代碼

踩坑:服務器

這裏要注意,我定義了Image對象的onerror事件,由於async await等待的Promise不知道捕獲到服務器錯誤的(若是服務器宕機了一下,腳本會直接中止報錯),因此咱們須要在onerror裏面引入,而後reject掉,promise就能夠拿到錯誤信息了,以後再catch就行了。同時爲了防止出現複雜的報錯中止,加入了try catch保證錯誤能被拿到。異步

相關文章
相關標籤/搜索