实现一个 Code Pen:(六)云函数生成网页缩略图

sxkk20082年前知识分享223

前言

在前面的文章中,我们已经实现了编辑器的功能,并且数据可以保存到云数据库,接下来我们需要生成缩略图的功能,目前掘金的的 code pen 还没有缩略图的功能,这是否是一个挑战呢?

缩略图生成方法

生成缩略图的方法可分为 2 种,一种是客户端生成,还有一种是服务端生成。

dom-to-img

客户端生成我第一个想到的是使用到一个库 dom-to-img

这个库,其主要原理是:

  • 将 html node 转化为 xml,设定命名空间

  • foreignObject 包裹 xml

  • 把内容变为了 svg

  • svg 变为 base64 的图片

    下面代码是最核心的源码中的一个函数 makeSvgDataUri

function makeSvgDataUri(node, width, height) {
  return Promise.resolve(node)
    .then(function (node) {
      node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
      return new XMLSerializer().serializeToString(node)
    })
    .then(util.escapeXhtml)
    .then(function (xhtml) {
      return '' + xhtml + ''
    })
    .then(function (foreignObject) {
      return (
        '' +
        foreignObject +
        ''
      )
    })
    .then(function (svg) {
      return 'data:image/svg+xml;,' + svg
    })
}

使用这个库的好处是可以通过客户端生成,节省服务端资源。但不足的是用户的浏览器大小不一,所生成的图片大小也不一样, 所以在我们 code pen 缩略图场景中,客户端生成不合适。

Puppeteer

服务端生成缩略图,我想到的是使用 Puppeteer 生成网页截图,来到达生成缩略图的效果。

Puppeteer 可以将 Chrome 或者 Chromium 以无界面的方式运行(当然也可以运行在有界面的服务器上),然后可以通过代码控制浏览器的行为,即使是非界面的模式运行,Chrome 或 Chromium 也可以在内存中正确渲染网页的内容。

vercel

由于我使用的是 vercel 部署的,那么我们是否可以使用 vercel 来生成缩略图吗?

我在一顿搜索之后找一篇文章 《Generate Open Graph images on-demand with Next.js on Vercel》

核心代码如下。

import chromium from 'chrome-aws-lambda'
import playwright from 'playwright-core'

const getAbsoluteURL = (path) => {
  const baseURL = process.env.VERCEL_URL
    ? `https://${process.env.VERCEL_URL}`
    : 'http://localhost:3000'
  return baseURL + path
}

export default async function handler(req, res) {
  // Start the browser with the AWS Lambda wrapper (chrome-aws-lambda)
  const browser = await playwright.chromium.launch({
    args: chromium.args,
    executablePath: await chromium.executablePath,
    headless: chromium.headless,
  })
  // 创建一个页面,并设置视窗大小
  const page = await browser.newPage({
    viewport: {
      width: 1200,
      height: 630,
    },
  })
  // 从url path 拼接成完成路径
  const url = getAbsoluteURL(req.query['path'] || '')
  await page.goto(url, {
    timeout: 15 * 1000,
    waitUntil: 'networkidle',
  })
  await page.waitForTimeout(1000)
  // 生成png 的缩略图
  const data = await page.screenshot({
    type: 'png',
  })
  await browser.close()
  // 设置图片强缓存
  res.setHeader('Cache-Control', 's-maxage=31536000, stale-while-revalidate')
  res.setHeader('Content-Type', 'image/png')
  // 设置返回 Content-Type 图片格式
  res.end(data)
}

chrome-aws-lambda 是为了 serverless 环境定制的 chrome 内核,包大小比较小,我将这段代码部署上去,通过 url 拼接的方式访问,我们就可以生成当前页面的缩略图了;

大家可以通过 https://code.runjs.cool/api/thumbnail?path=/pen/create 这个地址访问体验。 虽然有点慢,但是可以生成缩略图

vercel 生成的缩略图

有个问题就是,右上角的“保存”无法显示,查了下github chrome-aws-lambda 不包含任何字体,所以要支持中文,先要加载中文字体

readme 中有 demo

await chromium.font('/var/task/fonts/NotoColorEmoji.ttf')
// or
await chromium.font('https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf')

字体可以下载阿里巴巴普惠体,由于中文字体比较大,我就没有尝试,既然速度慢,那能否试试国内的云环境呢?

uniapp

由于我使用的云存储是 uniapp,那么我将尝试下 uniapp 的云函数。

本地尝试

于是我建立了一个云函数,然后在本地运行云函数。

首先安装使用 npm 安装 puppeteer npm i puppeteer

输入云函数代码

const puppeteer = require('puppeteer')

exports.main = async (event, context) => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto('https://baidu.com')
  const path = `${__dirname}/img.png`
  await page.screenshot({ path })
  await browser.close()
  return 1
}

执行完成后就在本地生成img.png文件,效果如下图

puppeteer 百度首页截图

没错,生成的是一张800*600 大小的浏览器截图,感觉没问题了。

阿里云

于是我就按这个逻辑写完了云函数,当我点击上传部署 的时候,HbuildX 就没有进度,一直处于上传中,查了下资料,应该是 puppeteer 本身依赖了 Chromium,Chromium 又依赖非常多的系统库,无法在云函数上安装完成。

但是我在阿里云官网找到了一篇文章

《Serverless 实战 —— 快速开发一个分布式 Puppeteer 网页截图服务》

按这篇文章讲述的是阿里云是支持 Puppeteer,由于 puppeteer 比较大,云函数会自动开通 NAS 服务(文件存储)

所以 uniapp 中选择服务商选择阿里云,云函数式不支持 puppeteer 的

腾讯云

那么腾讯云支持吗?后来我又查到腾讯云云函数中内置了 puppeteer,可以在文档中找到,注意(nodejs 16)已经不支持 puppeteer

于是我又尝试了腾讯云函数,代码如下

const puppeteer = require('puppeteer')

exports.main = async (event, context) => {
  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  })
  const page = await browser.newPage()
  await page.goto('https://baidu.com')
  const res = await page.screenshot()
  await browser.close()

  return {
    isBase64Encoded: true,
    statusCode: 200,
    headers: {
      'content-type': 'image/png',
      'Cache-Control': 's-maxage=31536000, stale-while-revalidate',
    },
    body: res.toString('base64'),
  }
}

此时不需要 package.json,上传后,云函数 URL 化,

体验地址:

https://tcb-mtsm4smjc8dbf7-8dkm6932475ab.service.tcloudbase.com/baidu

效果

腾讯云函数 puppeteer 缩略图

应该是满足了我们的需求。

小结

本文介绍了生成缩略图的方式

  1. dom-to-img 客户端生成,但是用户的浏览器大小不一,缩略图大小不一样。

  2. vercel 可以配合 chrome-aws-lambda 可以生成缩略图,但是必须要先加载字体。

  3. uniapp 免费的阿里云函数不支持 Puppeteer,可以直接使用阿里云的 serverles 服务,但是要开通 NAS。

  4. 腾讯云函数系统内置 Puppeteer,免安装,应该是比较不错的方案。

最后贴一下我项目地址和代码

预览地址:https://code.runjs.cool/pen/create

代码仓库:https://github.com/maqi1520/next-code-pen

以上就是本文全部内容,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

本文首发掘金平台,来源Ai知识分享博客

相关文章

华为AI芯片:引领人工智能新时代

华为AI芯片:引领人工智能新时代

  随着人工智能技术的不断发展,AI芯片作为人工智能的核心驱动力,成为众多科技公司竞相追逐的焦点。而在这个激烈的竞争环境中,华为凭借自身强大的实力和技术优势,成功研发出一系列...

Remix 对比 Next.js :一文深度解析

前言Next.js 是用于服务器端渲染的最流行的 React 框架之一,它流行了很多年,并且被许多开发者所喜爱。然而,随着 Remix 的开源引入, 开发人员已经开始怀疑哪个框架对他们的应用程序来说更...

AI换脸技术的发展与应用——改变面孔的科技革新

AI换脸技术的发展与应用——改变面孔的科技革新

    随着人工智能技术的快速发展,AI换脸技术成为了一个备受关注的热门话题。这项技术结合了深度学习、计算机视觉和图像处理等领域的研究成果,可以实现将一个人的面孔无缝地替换成...

人工智能专业学什么?探寻智能科技的未来发展前景!

人工智能专业学什么?探寻智能科技的未来发展前景!

  随着人工智能技术的快速发展与普及,越来越多的人开始对人工智能专业产生了浓厚的兴趣。那么,人工智能专业到底学什么呢?  一、学术理论基础:人工智能的核心要素  人工智能专业...

AI技术助力,智能OCR识别接口的新媒体探索与价值解读

AI技术助力,智能OCR识别接口的新媒体探索与价值解读

  在信息大爆炸的时代,海量的文字、图片和文档在网络中快速传播,而其准确、高速的识别成为了当代社会的一个重要挑战。为了满足这一需求,新媒体科技不断地推陈出新,其中尤为引人注目...

人工智能为艺术创作注入新生命

人工智能为艺术创作注入新生命

  随着科技的日新月异,人工智能技术越来越普及。在艺术创作领域,AI绘画已成为一个备受瞩目的领域,它不仅降低了艺术门槛,带来了更多的艺术可能性,还推动了艺术形式的不断创新和发...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。