手把手教您将 Ghostscript 移植到函数计算平台

720 阅读3分钟

前言

首先介绍下在本文出现的几个比较重要的概念:

函数计算(Function Compute):函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考Fun:Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考Ghostscript:Ghostscript 是一套建基于Adobe、PostScript及可移植文档格式(PDF)的页面描述语言等而编译成的自由软件。参见维基百科词条

备注: 本文介绍的技巧需要 Fun 版本大于等于 3.0.0-beta.2 。

依赖工具

本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。

Fun 工具依赖于 docker 来模拟本地环境。

对于 MacOS 用户可以使用 homebrew 进行安装:

brew cask install docker
brew tap vangie/formula
brew install fun

Windows 和 Linux 用户安装请参考:

  1. github.com/aliyun/fun/…

安装好后,记得先执行 fun config 初始化一下配置。

注意, 如果你已经安装过了 fun,确保 fun 的版本在 3.0.0-beta.2 以上。

$ fun --version
3.0.0-beta.2

Linux 下将 PDF 转换成 JPG

函数计算默认的 linux 环境是 Debian Jessie,首先我们先解决 Debain Jessie 环境下如何将 PDF 文件转换成 JPG 文件的问题。使用 fun install sbox 启动一个沙箱环境,在沙箱环境进行如下操作:

$ fun install sbox -r nodejs10 -i
root@fc-nodejs10:/code# apt-get update && apt-get install -y ghostscript
....
root@fc-nodejs10:/code# ls
test.pdf
root@fc-nodejs10:/code# gs -sDEVICE=jpeg -dTextAlphaBits=4 -r144 -o test.jpg test.pdf
GPL Ghostscript 9.26 (2018-11-20)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Processing pages 1 through 1.
Page 1
root@fc-nodejs10:/code# ls
test.jpg  test.pdf

其中转换命令为 gs -sDEVICE=jpeg -dTextAlphaBits=4 -r144 -o test.jpg test.pdf,其选项为

  • -sDEVICE=jpeg 指定输出设备为 jpeg,另外一个可选值为 jpeggray
  • -o 指定输出文件
  • -dTextAlphaBits 指定文本采样抗锯齿,另一个类似的选项为 -dGraphicsAlphaBits 用于图像采样抗锯齿。
  • -r144 设定图像的 dpi 为 144。

更多选项参见 www.ghostscript.com/doc/9.21/De…

移植到函数计算

下面我们将上面在 sbox 中 ghostscript 转换的实验成果移植到函数计算的 nodejs10 runtime 中。首先初始化一个本地的 fun 项目,借助于 fun 可以本地方便地安装依赖,本地调试并打包上传。

初始化项目

$ fun init event-nodejs10
Start rendering template...
+ /Users/vangie/Desktop/test
+ /Users/vangie/Desktop/test/.funignore
+ /Users/vangie/Desktop/test/index.js
+ /Users/vangie/Desktop/test/template.yml
finish rendering template.

将 template.yml 的内容更新为

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  ghostscript:
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'helloworld'
    pdf2jpg:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        Runtime: nodejs10
        CodeUri: './'
        EnvironmentVariables:
          GS_LIB: ".fun/root/usr/share/ghostscript/9.26/Resource/Init:\
            .fun/root/usr/share/ghostscript/9.26/lib:\
            .fun/root/usr/share/ghostscript/9.26/Resource/Font:\
            .fun/root/usr/share/ghostscript/fonts:\
            .fun/root/var/lib/ghostscript/fonts:\
            .fun/root/usr/share/ghostscript/fonts:\
            .fun/root/usr/share/fonts"

其中环境变量 GS_LIB 是为了支持 ghostscript 安装在代码目录所需要的。

将 index.js 的内容更新为

const { exec } = require('child_process');

module.exports.handler = function (event, context, callback) {
  const cmd = 'gs -sDEVICE=jpeg -dTextAlphaBits=4 -r144 -o /tmp/test.jpg test.pdf';
  exec(cmd, (err, stdout, stderr) => {
    if (err) {
      console.log(stdout);
      console.log(stderr);
      callback(err, "convert fail.\n");
    } else {
      console.log(stdout)
      callback(null, 'convert success.\nJPG file save to /tmp/test.jpg\n');
    }
  });
};

安装 ghostscript

使用如下命令安装 ghostscript

$ fun install -p apt -r nodejs10 ghostscript --save

ghostscript 会被安装到当前目录下的 .fun 目录内

$  tree . -a -L 4
.
├── .fun
│   └── root
│       ├── etc
│       │   ├── fonts
│       │   ├── ghostscript
│       │   └── libpaper.d
│       ├── usr
│       │   ├── bin
│       │   ├── lib
│       │   ├── sbin
│       │   └── share
│       └── var
│           └── lib
├── .funignore
├── README.md
├── fun.yml
├── index.js
└── template.yml

16 directories, 7 files

本地测试

在代码目录放一个 test.pdf 文件

$  tree          
.
├── README.md
├── fun.yml
├── index.js
├── template.yml
└── test.pdf

然后通过 fun local invoke 本地调用

$  fun local invoke pdf2jpg     
using template: template.yml
FC Invoke Start RequestId: 3ea14d81-fd6b-4259-b9a5-dde29c2f022a
load code for handler:index.handler
2019-09-03T07:36:20.200Z 3ea14d81-fd6b-4259-b9a5-dde29c2f022a [verbose] stdout =================== START
2019-09-03T07:36:20.200Z 3ea14d81-fd6b-4259-b9a5-dde29c2f022a [verbose] GPL Ghostscript 9.26 (2018-11-20)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Warning: the map file cidfmap was not found.
Processing pages 1 through 1.
Page 1

2019-09-03T07:36:20.201Z 3ea14d81-fd6b-4259-b9a5-dde29c2f022a [verbose] stdout =================== END
FC Invoke End RequestId: 3ea14d81-fd6b-4259-b9a5-dde29c2f022a
convert success.
JPG file save to /tmp/test.jpg
2019-09-03T07:36:20.212Z 3ea14d81-fd6b-4259-b9a5-dde29c2f022a [error] (node:23) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.


RequestId: 3ea14d81-fd6b-4259-b9a5-dde29c2f022a          Billed Duration: 1590 ms        Memory Size: 1998 MB    Max Memory Used: 32 MB

生成的 test.jpg 文件可以在 .fun/tmp/invoke 目录下找到

$  tree .fun/tmp/invoke -L 3
.fun/tmp/invoke
└── ghostscript
    └── pdf2jpg
        ├── agenthubout.log
        └── test.jpg

部署并调用

通过 fun deploy 命令快捷部署到函数计算平台

$  fun deploy              
using template: template.yml
using region: cn-shanghai
using accountId: ***********4733
using accessKeyId: ***********EUz3
using timeout: 60

Waiting for service ghostscript to be deployed...
        Waiting for function pdf2jpg to be deployed...
                Waiting for packaging function pdf2jpg code...
                package function pdf2jpg code done, the number of files you have packaged is:1048
        function pdf2jpg deploy success
service ghostscript deploy success

使用 fun invoke <function_name> 调用函数。

$  fun invoke pdf2jpg
using template: template.yml
========= FC invoke Logs begin =========
FC Invoke Start RequestId: 99cc8b32-6084-4a6b-a1ff-444e06e10eca
load code for handler:index.handler
2019-09-03T07:49:47.454Z 99cc8b32-6084-4a6b-a1ff-444e06e10eca [verbose] stdout =================== START
2019-09-03T07:49:47.455Z 99cc8b32-6084-4a6b-a1ff-444e06e10eca [verbose] GPL Ghostscript 9.26 (2018-11-20)
Copyright (C) 2018 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Warning: the map file cidfmap was not found.
Processing pages 1 through 1.
Page 1

2019-09-03T07:49:47.455Z 99cc8b32-6084-4a6b-a1ff-444e06e10eca [verbose] stdout =================== END
FC Invoke End RequestId: 99cc8b32-6084-4a6b-a1ff-444e06e10eca

Duration: 526.16 ms, Billed Duration: 600 ms, Memory Size: 128 MB, Max Memory Used: 57.09 MB
========= FC invoke Logs end =========

FC Invoke Result:
convert success.
JPG file save to /tmp/test.jpg

参考阅读

  1. 源码项目