如何通过 HTML 创建图片以创建动态复合信息卡

复合信息卡是 RCS Business Messaging 中最强大、最多样化的内容类型之一。借助复合信息卡,您可以在一条消息中发送大量相关信息,包括图片或视频、标题、说明,以及最多四条建议的回复或操作。

独立复合信息卡和复合信息卡轮播界面是一种强大的功能,可帮助您为用户打造引人入胜的体验。只要您要分享的图片是静态图片(如优惠券或产品),这些广告就非常实用。但是,如果您需要根据用户的相关信息(例如登机牌)发送动态内容,该怎么办?

在本文中,我们将向您介绍如何使用 HTML 即时创建图片,以及如何将这些图片嵌入到代理发送的复合信息卡中。我们先来看看如何将 HTML 转换为图片。

从 HTML 到图片

HTML 非常适合布局文字和媒体。作为开发者,如果我们要构建一个产品来向用户发送登机牌、数据使用情况图表或任何其他类型的用户特定信息之类的内容,我们可能会使用动态生成的 HTML。在 RCS Business Messaging 中,复合信息卡仅支持图片和视频媒体类型,因此为了将 HTML 的强大功能用于生成动态内容,首先需要将 HTML 转换为图片。

幸运的是,大多数现代编程语言都支持用于截取网页或组件屏幕截图的网页库(例如 JEditorPane),您可以使用这些库生成图片。

还有一些 API 可用于拍摄网页照片。例如,Google 的 Insights API 可以通过网址自动生成屏幕截图。

例如:

https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=https://www.google.com&screenshot=true

接下来,我们使用 Node.js Express 应用通过 HTML 生成用户专用登机牌,将其转换为图片,将其上传到可公开访问的网址,然后将图片附加到复合信息卡中以发送给用户。

创建动态登机牌

首先,我们需要 HTML 生成如下所示的登机牌。

登机牌示例

有多种方法可以实现这一点,但我们将在 Node.js 应用中定义一个名为 /getTicket?msisdn=+12223334444 的网址端点,以用于使用 EJS 视图引擎呈现登机牌所需的 HTML。

假设有一个名为 getUserTicket 的函数,该函数接受用户的电话号码并返回票券对象,该对象包含出发时间、座位、票券类型、出发地点等信息。

在 Express 应用的路由器中,我们定义 getTicket 端点,调用 getUserTicket 函数并将工单传递给 EJS 模板。

router.get('/getTicket', function(req, res, next) {
    // Get the passed in phone number
    let msisdn = req.body.msisdn;

    // Get the ticket object for this user
    let ticket = getUserTicket(msisdn);

    // Render the ticket in HTML using an EJS template
    res.render('ticket', {ticket: ticket});
});

票券 EJS 模板定义以下 HTML 来呈现示例 Bonjour 铁路票。

<div>
  <div>
    <div class="left"><img width="200" src="/images/bonjour-rail-logo.png" /></div>
    <div class="right muted-text"><%= ticket.dateOfDeparture; %></div>
    <div class="cl"></div>
  </div>
  <div>
    <div class="left highlighted-text-color"><h2><%= ticket.startingLocation.city %></h2><br/><%= ticket.startingLocation.station %></div>
    <div class="right highlighted-text-color"><h2><%= ticket.endingLocation.city %></h2><br/><%= ticket.endingLocation.station %></div>
    <div class="cl"></div>
  </div>

  <div>
    <div class="left">
        <h3>PLATFORM <%= ticket.platform %></h3>
      <div>
        <div class="left">
          <div><h4>DEPART</h4></div>
          <div><%= ticket.departureTime %></div>
        </div>
        <div class="left">
          <div><h4>ARRIVE</h4></div>
          <div><%= ticket.arrivalTime %></div>
        </div>
        <div class="cl"></div>
      </div>
      <div>
        <div class="left">
          <div><h4>TICKET TYPE</h4></div>
          <div><%= ticket.ticketType %></div>
        </div>
        <div class="left">
          <div><h4>SEAT</h4></div>
          <div><%= ticket.seat %></div>
        </div>
        <div class="cl"></div>
      </div>
    </div>
    <div class="right">
      <img src="<%= ticket.qrCode %>" width="170" />
    </div>
    <div class="cl"></div>
  </div>
</div>

通过 HTML 创建图片

我们定义了用于构建登机牌的 HTML,但我们需要一种将其转换为图片的方法。

有许多开源 Node.js 模块可用于将原始 HTML 和网址转换为图片。在本示例中,我们将使用 node-webshot,它是 PhantomJS 的轻量级封装容器。

PhantomJS 是一款可通过脚本执行的无头浏览器,可将 HTML 呈现为图片。PhantomJS 依靠 WebKit 进行渲染,因此可以处理包含图片、CSS 和 JavaScript 的复杂网页。

安装节点网络拍摄 (npm install --save node-webshot) 后,将 HTML 转换为图片非常简单。

var webshot = require('node-webshot');
let url = '/getTicket?msisdn=' + msisdn;
let htmlAsImageFileLocation = 'ticket.png';

// Define screenshot options
var options = {
      screenSize: {
            width: 550,
            height: 450
      },
      shotSize: {
            width: 550,
            height: 450
      },
      userAgent: 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.20 (KHTML, like Gecko) Mobile/7B298g'
    };

// Save html as an image
webshot(url, htmlAsImageFileLocation, options, function(err) {
  // TODO: Upload image to a publicly accessible URL
});

将文件存储到可公开访问的网址

为了使用通过网络拍摄生成的图片,我们需要一个可公开访问的网址。如果您要在专用网络服务器上生成图片,只需将文件保存到公共文件夹即可,但在此示例中,我们要将文件上传到 Google Cloud Storage

以下函数接受本地图片位置并将其上传到 Google Cloud Storage,并返回新创建的媒体链接。

function uploadImage(imageLocation) {
    const {Storage} = require('@google-cloud/storage');

    // Creates a client
    const storage = new Storage();

    // Define the Cloud storage location and file name
    const bucketName = 'sample-image-uploads';
    const yourFileDestination = 'uploaded-image.png';

    // Set the options for the upload to be readable by anyone
    const options = {
        destination: yourFileDestination,
        predefinedAcl: 'publicRead'
    };

    // Return a promise that includes the storage upload
    return new Promise(function(resolve, reject) {
        // Uploads a local file to the bucket
        storage.bucket(bucketName).upload(imageLocation, options)
            .then(results => {
                // Return the image URL
                resolve(results[0].metadata.mediaLink);
            }).catch(error => {
                reject(error);
            });
    });
}

接下来,我们需要将之前评论的 TODO 替换为使用此 uploadImage 函数的调用。

// Save html as image
webshot(url, htmlAsImageFileLocation, options, function(err) {
      let uploadRequest = uploadImage(filename);

      uploadRequest.then(function(imageUrl) {
         // TODO: Use the imageUrl in a rich card to send to the user
      });
});

发送包含图片的复合信息卡

即将大功告成。我们来定义 sendBoardingPass(imageUrl, msisdn) 函数,使用在上一步中创建的图片创建 RBM 复合卡片并将其发送给用户,从而完成最后一步。

为了使用 RBM 发送复合信息卡,我使用第一个代理示例中提供的 Node.js SDK。

function sendBoardingPass(imageUrl, msisdn) {
    let title = 'Here is your boarding pass.';

    let suggestions = [
                {
                    reply: {
                        text: 'See more options',
                        postbackData: 'more_options',
                    }
                },
            ];

    let params = {
        messageText: title,
        msisdn: msisdn,
        imageUrl: imageUrl,
        suggestions: suggestions,
        height: 'TALL',
    };

    // Send the boarding pass
    rbmApiHelper.sendRichCard(params);
}

以下是最终结果的屏幕截图。

复合信息卡中的动态图片

总结与要点

为了让复合型卡片用途更丰富,最简单的方法是利用动态 HTML,并将 HTML 转换为图片。大多数现代编程语言都支持可帮助您完成转换的库或 API。您可能需要尝试调整图片大小,以获得适合您用例的内容,但这是一种在 RBM 代理中制作更具吸引力的视觉效果的妙招。

祝您好运,编码愉快!