[入門到吐槽系列] 微信小程序 上傳圖片 前端壓縮 保存到雲存儲 使用Canvas新API的巨坑!


前言:

繼續接着上次用戶提交個性化頭像圖片,服務端審核的需求,這次是前端的巨坑部分。10分鍾避坑指南分享。

 

避坑過程分享:

我們的小程序是這個樣子的:

 

 

 

現在的需求是,長按頭像后,可以選擇自定義的圖片,更換頭像。這么需求就特么簡單一句話,又足足搞了我12個小時。

 

技術思路:

  • 使用wx.chooseImage選擇圖片
  • 使用canvas對圖片進行尺寸變更,前端壓縮
  • 使用canvasToTempFilePath對壓縮有的圖片保存到臨時目錄
  • 使用wx.cloud.uploadFile提交到雲存儲,然后異步讓微信校驗

 

選擇用戶自定義圖片 chooseMedia:

當你想使用chooseImage的時候,突然發現IDE提示這個接口不再維護。恩。。。不詳的預感。畢竟現在網上搜的一大堆文檔,都是用chooseImage,所以現在最新代碼是:

 wx.chooseMedia({
        count: 1,
        sizeType: ['compressed'], // original 原圖   compressed 壓縮圖
        sourceType: ['album', 'camera'], // album 從相冊選圖    camera  使用相機
      }).then(e1 => {
        console.log('圖像處理', 'chooseMedia', e1);
        if (e1.tempFiles.length <= 0)
          return;
      });

 

獲取圖片的信息 getImageInfo:

接下來要進行前端壓縮,先獲取圖片的尺寸信息:

      wx.chooseMedia({
        count: 1,
        sizeType: ['compressed'], // original 原圖   compressed 壓縮圖
        sourceType: ['album', 'camera'], // album 從相冊選圖    camera  使用相機
      }).then(e1 => {
        console.log('圖像處理', 'chooseMedia', e1);
        if (e1.tempFiles.length <= 0)
          return;

        wx.getImageInfo({
          src: e1.tempFiles[0].tempFilePath,
          success: function (e2) {
            const imgWidth = e2.width;
            const imgHeight = e2.height;
            const dpr = wx.getSystemInfoSync().pixelRatio * 2;
            const imgW = Math.trunc(imgWidth / dpr);
            const imgH = Math.trunc(imgW / imgWidth * imgHeight);
            console.log('圖像處理', 'getImageInfo', e2, dpr, [imgWidth, imgHeight], [imgW, imgH]);
          },
          fail: function (res) {
            console.log(res.errMsg)
          },
        });

 

這里使用了屏幕的pixelRatio后,發現圖片還是不夠小,只是用來做頭像,所以我們就使用2倍的pixelRatio。

 

巨坑1——獲取Canvas:

當我看資料,說用wx.createCanvasContext的時候,竟然提示被廢棄了。瞬間網上9成的資料都變成廢話。只要繼續慢慢摸索,使用wx.createSelectorQuery:

 

 

 

實際代碼,首先在頁面聲明一個canvas,id是必須的,其他的canvas-id之類已經沒意義了。類型type是2d。

  <canvas id="avatarCanvas" canvasId="avatarCanvas" type="2d"
    style="width:{{cWidth}}px;height:{{cHeight}}px;position: absolute;left:-10000px;top:-10000px;"></canvas>

然后后端使用選擇器獲取這個canvas:

      wx.chooseMedia({
        count: 1,
        sizeType: ['compressed'], // original 原圖   compressed 壓縮圖
        sourceType: ['album', 'camera'], // album 從相冊選圖    camera  使用相機
      }).then(e1 => {
        console.log('圖像處理', 'chooseMedia', e1);
        if (e1.tempFiles.length <= 0)
          return;

        wx.getImageInfo({
          src: e1.tempFiles[0].tempFilePath,
          success: function (e2) {
            const imgWidth = e2.width;
            const imgHeight = e2.height;
            const dpr = wx.getSystemInfoSync().pixelRatio * 2;
            const imgW = Math.trunc(imgWidth / dpr);
            const imgH = Math.trunc(imgW / imgWidth * imgHeight);
            console.log('圖像處理', 'getImageInfo', e2, dpr, [imgWidth, imgHeight], [imgW, imgH]);

            const query = wx.createSelectorQuery();
            query.select('#avatarCanvas').fields({
              node: true,
              size: true
            }).exec(e3 => {
              console.log('圖像處理', 'createSelectorQuery', e3);
              const canvas = e3[0].node;
              canvas.width = imgW;
              canvas.height = imgH;
              const ctx = canvas.getContext('2d');
              ctx.clearRect(0, 0, imgW, imgH);
              that.setData({
                cWidth: imgW,
                cHeight: imgH,
              });
            });
          },
          fail: function (res) {
            console.log(res.errMsg)
          },
        });
      });

必須注意,頁面要聲明type=2d,否則選擇器返回的node是個null!小坑。

然后canvas一定要設置width、height,同時也要設置setData,修改頁面的綁定的style的寬度高度,否則圖片會拉伸。

 

超級無敵巨坑2——canvas顯示上傳的圖片:

傳統看資料,把圖片畫到canvas就是ctx.drawImage,現在用了新的API之后,這些方法都廢了。新的api是:

              const img = canvas.createImage();
              img.onload = (e4) => {
                console.log('圖像處理', 'onload', e4);
                ctx.drawImage(img, 0, 0, imgW, imgH);
              }
              img.src = e2.path;

這里面有個小坑,drawImage第一個參數必須是創建的img對象節點,不再是舊api的圖片地址了。

那么放入整個代碼里面:

      wx.chooseMedia({
        count: 1,
        sizeType: ['compressed'], // original 原圖   compressed 壓縮圖
        sourceType: ['album', 'camera'], // album 從相冊選圖    camera  使用相機
      }).then(e1 => {
        console.log('圖像處理', 'chooseMedia', e1);
        if (e1.tempFiles.length <= 0)
          return;

        wx.getImageInfo({
          src: e1.tempFiles[0].tempFilePath,
          success: function (e2) {
            const imgWidth = e2.width;
            const imgHeight = e2.height;
            const dpr = wx.getSystemInfoSync().pixelRatio * 2;
            const imgW = Math.trunc(imgWidth / dpr);
            const imgH = Math.trunc(imgW / imgWidth * imgHeight);
            console.log('圖像處理', 'getImageInfo', e2, dpr, [imgWidth, imgHeight], [imgW, imgH]);

            const query = wx.createSelectorQuery();
            query.select('#avatarCanvas').fields({
              node: true,
              size: true
            }).exec(e3 => {
              console.log('圖像處理', 'createSelectorQuery', e3);
              const canvas = e3[0].node;
              canvas.width = imgW;
              canvas.height = imgH;
              const ctx = canvas.getContext('2d');
              ctx.clearRect(0, 0, imgW, imgH);
              that.setData({
                cWidth: imgW,
                cHeight: imgH,
              });

              const img = canvas.createImage();
              img.onload = (e4) => {
                console.log('圖像處理', 'onload', e4);
                ctx.drawImage(img, 0, 0, imgW, imgH);
              }
              img.src = e2.path;
            });
          },
          fail: function (res) {
            console.log(res.errMsg)
          },
        });
      });

這個時候,啟動調試,會發現報錯了!!!

tmp/wxf65e9ae5f68283d2.o6zAJs5h4IkyHaGS7_j6gUPGTR9c.arwyj04Eq2ok341457e272957e237fa21d743912f60b.jpg:1 GET http://tmp/wxf65e9ae5f68283d2.o6zAJs5h4IkyHaGS7_j6gUPGTR9c.arwyj04Eq2ok341457e272957e237fa21d743912f60b.jpg net::ERR_PROXY_CONNECTION_FAILED

對於這個問題的討論,再這里可以看到詳情:

https://developers.weixin.qq.com/community/develop/doc/00040e150384902e280a3a85e56800?highLine=drawimage%252C%2520%25E4%25B8%25B4%25E6%2597%25B6%25E5%259C%25B0%25E5%259D%2580

大概意思是image對象無法訪問臨時路徑。然后要修改下開發環境的代理:

 

 小編認為這是個騰訊的bug,但是也困擾了接近3個小時。反正改后就沒事了,可以看到日志輸出:

 

改后就不報錯了,可以進入image的onload事件。

 

壓縮圖片提交雲存儲:

最后簡單了,獲取壓縮后的圖片,提交雲存儲,核心代碼:

                const cfgSave = {
                  fileType: "jpg",
                  quality: 0.5,
                  width: imgW,
                  height: imgH,
                  destWidth: imgW,
                  destHeight: imgH,
                  canvas: canvas,
                };
                wx.canvasToTempFilePath({
                  ...cfgSave,
                }).then(e5 => {
                  console.log("圖像處理", 'canvasToTempFilePath', e5);
                  wx.cloud.uploadFile({
                    cloudPath: 'avatars/' + new Date().getTime() + ".jpg",
                    filePath: e5.tempFilePath,
                  }).then(e6 => {
                    console.log("圖像處理", 'uploadFile', e6);
                  });
                }).catch(err => {
                  console.error(err);
                })

 

整段完整的代碼:

 

      wx.chooseMedia({
        count: 1,
        sizeType: ['compressed'], // original 原圖   compressed 壓縮圖
        sourceType: ['album', 'camera'], // album 從相冊選圖    camera  使用相機
      }).then(e1 => {
        console.log('圖像處理', 'chooseMedia', e1);
        if (e1.tempFiles.length <= 0)
          return;

        wx.getImageInfo({
          src: e1.tempFiles[0].tempFilePath,
          success: function (e2) {
            const imgWidth = e2.width;
            const imgHeight = e2.height;
            const dpr = wx.getSystemInfoSync().pixelRatio;
            const imgW = Math.min(640, Math.trunc(imgWidth / dpr));
            const imgH = Math.trunc(imgW / imgWidth * imgHeight);
            console.log('圖像處理', 'getImageInfo', e2, dpr, [imgWidth, imgHeight], [imgW, imgH]);

            const query = wx.createSelectorQuery();
            query.select('#avatarCanvas').fields({
              node: true,
              size: true
            }).exec(e3 => {
              console.log('圖像處理', 'createSelectorQuery', e3);
              const canvas = e3[0].node;
              canvas.width = imgW;
              canvas.height = imgH;
              const ctx = canvas.getContext('2d');
              ctx.clearRect(0, 0, imgW, imgH);
              that.setData({
                cWidth: imgW,
                cHeight: imgH,
              });

              const img = canvas.createImage();
              img.onload = (e4) => {
                console.log('圖像處理', 'onload', e4);
                ctx.drawImage(img, 0, 0, imgW, imgH);
                const cfgSave = {
                  fileType: "jpg",
                  quality: 0.5,
                  width: imgW,
                  height: imgH,
                  destWidth: imgW,
                  destHeight: imgH,
                  canvas: canvas,
                };
                wx.canvasToTempFilePath({
                  ...cfgSave,
                }).then(e5 => {
                  console.log("圖像處理", 'canvasToTempFilePath', e5);
                  wx.cloud.uploadFile({
                    cloudPath: 'avatars/' + new Date().getTime() + ".jpg",
                    filePath: e5.tempFilePath,
                  }).then(e6 => {
                    console.log("圖像處理", 'uploadFile', e6);
                  });
                }).catch(err => {
                  console.error(err);
                })
              }
              img.src = e2.path;
            });
          },
          fail: function (res) {
            console.log(res.errMsg)
          },
        });
      });

 

雲存儲的結果:

 

 

整個流程分享完畢,接下來就是接到提交圖片給騰訊鑒黃流程了,可以參考上一篇文章。

 

歡迎大家關注咱們公眾號:辰同學技術 微信公眾號,同樣會分享各種技術10分鍾從入門到吐槽:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM