blog
  • README
  • php
    • swoole
      • manageProcess + redis 实现 ppt 转 pdf 服务
      • 基于 swoole 的多进程消息同步微服务 -- (3)实现 swoole 多进程消息同步微服务
      • 基于 swoole 的多进程消息同步微服务 -- (2)阿里云日志拉取实现与数据 check
      • manageProcess - 用php+swoole实现多进程管理类
      • 基于 swoole 的多进程消息同步微服务 -- (1)需求介绍和系统架构设计
    • php 信号处理
  • golang
    • golang 学习篇1 --- 实现一个简单的base64加解密
  • fromChat
    • 发红包问题
Powered by GitBook
On this page
  1. php
  2. swoole

manageProcess + redis 实现 ppt 转 pdf 服务

PreviousswooleNext基于 swoole 的多进程消息同步微服务 -- (3)实现 swoole 多进程消息同步微服务

Last updated 6 years ago

最近接到一个把 ppt 转成 pdf 的需求,和客户端约定 ppt 最大需要50M,用户上传ppt 文件到服务器,服务器经过一系列的处理搞定转化工作,最后客户端在将成品的pdf文件下载下来,写个博客记录下。 首先是技术选型:服务端有个 libreoffice 插件,可以完成 ppt 转 pdf 的功能,和客户端简单的测试了下,发现效果不错,来两张转化的效果图:

    可以看到效果还是相当不错的,源文件是我从网上随便 down 的,具体链接可以参考我下面的附录; 

接口设计:上传 upload 接口上传 ppt 文件;轮询 process 接口查询该文件处理进度;download 接口下载转换成功的 pdf 文件;

接下来是架构实现:客户端上传到服务端,文件上传嘛!服务端上传文件到客户端,表单上传搞定。框架现有的 upload 类轮子,我这吭哧吭哧改了一顿代码之后,自测通过,pass!(这里给自己留了个大坑,留待下一个模块慢慢说)

服务端处理,考虑到 php 处理一个服务是 php-fpm 进程搞定的,直接上手简直爆炸,把一个最大可能50M,最小也得好几M的文件放在 php-fpm 里面处理,一来可能导致当前的 php-fpm 进程阻塞,二来严重阻碍其他 request 请求的顺利执行,三来我们都晓得计算开启 php-fpm 进程数量很重要一个参考因素是 php-fpm 进程占服务器内存20-30m来算的;所以直接使用php-fpm进程搞定就被我们给否了,但是这个服务又不需要太多的并发流量; 考虑再三,使用redis list 实现一个队列,manageProcess 快速实现一个多进程任务,抢占式的 pop redis 队列,上传时创建全局 uuid 并使用 redis 记录处理的进度; 按照事先的接口设计实现代码,服务器设置下跨域,本地一个 form 表单上传完美搞定。

第二天,与客户端联调,本想在前端小姐姐面前秀一波,没想到第一步就卡了,upload 请求收不到,什么情况,查下nginx,发现请求来了,但是我没有正确反馈,wtf??? 细问下了解到原来客户端用的是nodejs,根本没有 form 表单一说(呼应开头的那个坑),好吧找到问题,那就现场撸代码,php 有个神奇的 php://input 自动获得http body 数据流,我直接强行读 php://input 然后写文件不就搞定了,perfect 解决! 还有其他字体的坑,不是本文核心就不一一列举了,贴下核心代码:

while (true) {
    $this->flushTimestamp($pid);
    $this->nowUuid = null;
    $fileInfo = $redis->rpop(self::prefix);
    if(!$fileInfo) {
        sleep(1);
        continue;
    }
    $output = '';
    $uuid = $fileInfo['uuid'];
    $this->nowUuid = $uuid;
    $savePath = $fileInfo['savePath'];
    $ext = $fileInfo['ext'];
    $filename = $savePath . $uuid . ".$ext";
    $res = [
        'status' => 2,
        'uuid' => $uuid,
        'msg' => 'wait file sync'
    ];
    $redis->set(self::prefix.'_'.$uuid,$res);
    if(!file_exists($filename)) {
        // 等待 NAS 服务同步数据
        $this->flushTimestamp($pid);
        sleep(3);
        if(!file_exists($filename)) {
            $res = [
                'status' => -1,
                'uuid' => $uuid,
                'msg' => 'file not found,please try again!'
            ];
            $redis->set(self::prefix.'_'.$uuid,$res);
            $fileInfo['__REASON__'] = 'file not exist';
            Fend_Log::write($fileInfo,$this->config['logName']);
            continue;
        }
    }
    if(md5_file($filename) != $fileInfo['filemd5']) {
        $res = [
            'status' => -2,
            'uuid' => $uuid,
            'msg' => 'file damage!'
        ];
        $redis->set(self::prefix.'_'.$uuid,$res);
        $fileInfo['__REASON__'] = 'file damage in nas!';
        Fend_Log::write($fileInfo,$this->config['logName']);
        continue;
    }
    $res = [
        'status' => 2,
        'uuid' => $uuid,
        'msg' => 'process transforming...'
    ];
    $redis->set(self::prefix.'_'.$uuid,$res);
    exec("/usr/bin/libreoffice  --invisible --convert-to pdf --outdir $savePath ".$filename,$output);
    $res = [
        'status' => 1,
        'execOut' => $output,
        'msg' => 'transform complete',
        'path' => $savePath,
        'name' => $uuid. ".$ext",
        'uuid' => $uuid,
        'ext' => $ext,
        'fileName' => $filename
    ];
    $redis->set(self::prefix.'_'.$uuid,$res);
}

附录:

1、pptx 模板文件:

2、转化完成的pdf连接:

3、参考的文章连接:

https://weizengreal.github.io/blog/resource/2018/01/job.pptx
https://weizengreal.github.io/blog/resource/2018/01/job.pdf
http://blog.csdn.net/ljihe/article/details/77250206