[入门到吐槽系列] 微信小程序 上传图片 前端压缩 保存到云存储 使用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分钟从入门到吐槽:

 

posted @ 2022-03-06 17:07    阅读(1110)  评论(1编辑  收藏  举报
IT民工