惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
W
WeLiveSecurity
O
OpenAI News
N
News and Events Feed by Topic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
N
News | PayPal Newsroom
H
Hacker News: Front Page
博客园_首页
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Last Watchdog
The Last Watchdog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Heimdal Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Schneier on Security
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Y
Y Combinator Blog
Cyberwarzone
Cyberwarzone
Microsoft Security Blog
Microsoft Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
GbyAI
GbyAI
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
人人都是产品经理
人人都是产品经理
P
Palo Alto Networks Blog
M
MIT News - Artificial intelligence
G
GRAHAM CLULEY
C
Check Point Blog
Apple Machine Learning Research
Apple Machine Learning Research
Last Week in AI
Last Week in AI
T
Troy Hunt's Blog
L
Lohrmann on Cybersecurity
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
量子位
博客园 - 聂微东
S
Securelist
博客园 - 三生石上(FineUI控件)
F
Full Disclosure
G
Google Developers Blog
L
LINUX DO - 热门话题
P
Proofpoint News Feed
AI
AI
PCI Perspectives
PCI Perspectives

博客园 - 辰

【破解】webix 9.1.6 试用版 弹窗提示破解教程 【经验】微信小程序 canvas 图片拉伸 对 wx.canvasToTempFilePath 截图导出的影响。 【经验】微信小程序Canvas 的画图尺寸学习研究。 [入门到吐槽系列] 微信小程序 敏感违规图片检测 mediaCheckAsync,客服接口 消息推送 的各种坑分享! [突发救援] 网站突发被腾讯云因未备案 屏蔽,60分钟内抢救恢复成功! [入门到吐槽系列] Webix 10分钟入门 二 表单Form的使用 [入门到吐槽系列] Webix 10分钟入门 一 管理后台制作 租了一台华为云耀云服务器,却直接被封公网ip,而且是官方的bug导致! 从事游戏行业也有10年,让我来说下游戏的本质,为什么上瘾。 Egret 生成 自带EUI 的微信小游戏 踩坑! JMeter入门 像屎一样的 Spring Boot入门,总算有反应了 亲测有效!一种完美动态阈值白平衡算法 Java实现。 Windows 2008 - 由于管理员设置的策略,该磁盘处于脱机状态 egret调用页面js的方法。 egret GUI 和 egret Wing 是我看到h5 最渣的设计 Egret 集成第三方库 记录 乐高情报站7月份抽奖数据汇总。 今天是第二个最值得纪念的日子。
[入门到吐槽系列] 微信小程序 上传图片 前端压缩 保存到云存储 使用Canvas新API的巨坑!
· 2022-03-06 · via 博客园 - 辰

前言:

继续接着上次用户提交个性化头像图片,服务端审核的需求,这次是前端的巨坑部分。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分钟从入门到吐槽: