小程序生成分享图片的一个方案 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Croath
V2EX    微信

小程序生成分享图片的一个方案

  •  3
     
  •   Croath 2019-10-24 18:27:15 +08:00 4578 次点击
    这是一个创建于 2178 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们最近开发了一些小程序,每次要上线之前都想快速添加到分享朋友圈功能,但是做了几次之后发现好麻烦啊,要么效果不好,要么成本高,要么不能动态更新,所以顺手做了这个解决方案出来,大家有什么问题或者想讨论的可以一起讨论:)欢迎建议:)以下正文:


    小程序生成图片分享朋友圈

    小程序开发者都希望自己的小程序得以广泛传播,因为不少小程序都设计了很多转发激励行为,但分享小程序到朋友圈(或其他外部平台)一直是一个难题。一个常见的方案就是生成分享海报、分享图片。但生成分享图片在技术上却也是一个难题。


    技术选型

    目前常用技术方案基本分为三种:

    1. 使用 canvas 绘图并生成
    2. 使用后端绘图库进行绘制,返回给小程序端
    3. 使用服务端开一个浏览器进行 HTML 渲染,并截图返回给小程序端

    第一种方案:要求较高,canvas 和纯 html 布局相去甚远,零基础学习成本较高,而且在不同的微信浏览器中效果不可预期,想短时间内做出精美可控的生成图片不容易。实操的时候发现了一个非常麻烦的事情:网络图片或者 base64 图片都无法直接在 canvas 里渲染显示,要先下载好传进去。

    第二种方案:后端库可以完成较为简单的需求,但字体加载、阴影、圆角、透明等方案效果需要精调,如果文字需要截断或动态伸缩长度时并不容易处理。图片的截取和伸缩自适应也不灵活。而且选用这种方案相当于需要把 UI 布局的工作丢给后端工程师去解决,这不是他们擅长的范围,效果未必会好。

    第三种方案:页面的绘制方面,纯前端技术即可完成,难度低,完成度高,但是需要在后端起一个 node 服务开启 puppeteer 去控制服务端 Chrome 浏览器。这种方案的缺点就是成本太高,我们和业界同行都测算过,结果差不多:4 核 16G 的服务器生成图片的 QPS 大概只有 10-20,相当于一秒钟较差情况只能生成 10 张图片,这对于突发的大量分享需求并不能满足,而且这种配置的服务器,不能部署其他服务,只跑这个服务就会用尽大部分资源。

    费用上:只单单算 5M 带宽的服务器费用一个月就要 700+ 人民币,流量和图片托管费用另算。此方案的最小化实现:至少需要 1 核 2G 的服务器才能较为顺畅地完成一次顺利截图,但是还是要处理浏览器无响应假死等情况,较为复杂。但综合来看,这种方案是效果最好最为灵活的。

    快海报小程序分享图生成服务

    快海报 kuaihaibao.com 是专门提供小程序分享海报生成服务的,技术上用的就是上面所述的第三种方案,但是只需要调用他的 API 就可以完成,不需要开发者维护 puppeteer 和 headless Chrome,而且成本较低,一张分享图的最低生成成本是 0.033 元。

    其实真正集成到自己的服务中时,平均成本要比这个低,因为有些生成的图片的二维码,如果不带用户个人信息(不给分享的用户返利)时,可以生成一次之后永久缓存起来,其他用户再分享同一个东西都用缓存好的图片,综合成本就降下来了。

    算一下成本:

    • 比方说一个刚起步的小程序日活 5000 (对于刚起步的小程序其实已经很高了吧)
    • 假设有 5% 的用户生成分享图
    • 也就是每天生成 250 张分享图,一个月会生成 7500 张分享图

    这样的话每个月成本就是 250 元人民币左右,相比 700+ 人民币的服务器成本省太多了。这是测算比较高的指标,而且是完全不应用缓存方案的情况。

    如果你的小程序还处于冷启动的阶段:

    • 日活 500
    • 假设有 5% 的用户生成分享图
    • 也就是每天生成 25 张分享图,一个月会生成 750 张分享图

    每月成本 25 元。比 1 核 2G 的最小化自部署方案也要便宜。但带来的收益是无穷的,750 张分享图发到朋友圈,每张分享图 1000 受众浏览,一个月就是将近 750000 人次分享受众。

    调用 API

    首先去 https://kuaihaibao.com/ 注册账号,验证邮箱激活之后,其实就可以先测试用了,每个账号有 100 次测试额度,测试生成的图片带水印。

    网站左侧的 [文档] 页面能找到集成文档,非常简单,一共就只有一个核心 API,通过 HTTP 调用的。

    先在 [开发] -> [设置] 中激活 token

    image.png

    然后打开 [开发] -> [模板] 中,找到自己喜欢的模版。因为我只想生成一个简单的分享图片,所以最简单的方式就是使用网站内置的模版,内置模板目前有 8 款,应该能满足大部分小程序的需求了,抽奖、打卡、图文、文字、电商都有,改一改文案和图片就可以了。

    我选了这个抽奖模板:

    image.png

    按照 https://kuaihaibao.com/doc/docs/template/kzccda95.html 文档描述的 JSON 改成我需要的:

    { "backgroundColor": "#fafafa", "backgroundImage": "", "user": { "avatar": "https://khb-sample.oss-cn-shanghai.aliyuncs.com/sample/girl_2.jpg", "nickname": "我是测试账号", "color": "#666" }, "tip": "邀请你来抽奖", "qrcode": "https://khb-sample.oss-cn-shanghai.aliyuncs.com/sample/sample_qr_0.png", "records": [ { "title": "一等奖", "desc": "2019 年 11 月 16 日 10:00 开奖", "image": "https://s3.cn-northwest-1.amazonaws.com.cn/res.weiyidan.com/production/10000002/4109f8e51a8f43b9816dbc8fe636e22a.jpeg" } ], "brand": "我的测试抽奖小程序", "slogan": "快来和我一起抽吧!", "metaColor": "#999" } 

    然后打开 Terminal 做一次请求试试:

    curl -X "POST" "https://api.kuaihaibao.com/services/screenshot" \ -H 'Authorization: Bearer 这里写你自己的 token' \ -H 'Content-Type: application/json; charset=' \ -d /pre>{ "template": "kzccda95", "data": { "qrcode": "https://khb-sample.oss-cn-shanghai.aliyuncs.com/sample/sample_qr_0.png", "records": [ { "title": "一等奖", "desc": "2019 年 11 月 16 日 10:00 开奖", "image": "https://s3.cn-northwest-1.amazonaws.com.cn/res.weiyidan.com/production/10000002/4109f8e51a8f43b9816dbc8fe636e22a.jpeg" } ], "tip": "邀请你来抽奖", "slogan": "快来和我一起抽吧!", "metaColor": "#999", "brand": "我的测试抽奖小程序", "backgroundImage": "", "backgroundColor": "#fafafa", "user": { "avatar": "https://khb-sample.oss-cn-shanghai.aliyuncs.com/sample/girl_2.jpg", "nickname": "我是测试账号", "color": "#666" } } }' 

    返回了结果:

    { "success": true, "data": { "name": "iPhone 5", "image": "https://khb-test-oss.oss-cn-shanghai.aliyuncs.com/screenshot/4fa63f2a3605cbdece90c659cbccea619d9cf9fa?x-oss-process=style/test_watermark" } } 

    打开图片地址看看:

    image.png

    速度很快,图片很漂亮,只是中间带水印,充值后成为付费用户,再生成的图片水印就自动取掉了。

    后端集成

    这里参考官方文档最佳实践的逻辑参考图:

    image.png

    所以后端只需要做一件事,就是提供一个 API 给客户端用,这个 API 被调用的时候去请求快海报的服务器,再把结果返回给小程序就好了。

    airyland
        1
    airyland  
       2019-10-24 18:31:45 +08:00
    考虑过这个东西,相比于按次数收费,独立部署的形式应该是更适合用户的。

    你的文档页面有 bug,点击是直接下载页面。
    Croath
        2
    Croath  
    OP
       2019-10-24 18:59:04 +08:00 via iPhone
    @airyland 独立部署其实比较浪费资源,成本就上去很多。

    感谢提醒,今天 CDN 节点事故了刚恢复可能有缓存,我去刷新一下。
    Croath
        3
    Croath  
    OP
       2019-10-24 19:13:10 +08:00
    其实是一个成本权衡的事情

    1. 大型团队都能自己解决这个问题,无需依赖外部服务(其实调研起来我很惊讶,观察到很多大型团队也没有提供解决方案)
    2. 小型团队,如果有能力自己部署的话,专人维护+专用服务器,无论是硬件成本还是人力成本都很高,独立部署其实不利于节约成本,对小型团队来说是节约资源的
    3. 独立开发者 /个人作品等,综合成本非常低,技术难度低,是快速上手的好选择
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5553 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 08:52 PVG 16:52 LAX 01:52 JFK 04:52
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86