使用openoffice或liboffice软件soffice命令转pdf

背景

  1. 最近的公司有一个需求,需要在线预览office(xls ppt doc)文件, 之前使用了微软的office在线预览服务,https://view.officeapps.live.com/op/view.aspx?src=文档地址, 这样就可以在线预览,但是该网站有文件大小限制(xls小于5m, ppt doc 小于10m),因此需要自己实现一种预览服务或者使用第三方的预览服务,第三方预览服务使用方式类似上述微软,需要收费,个人实现方式也有很多,这里只说我实现的方式。

实现在线预览服务

  1. 用户上传office文件,
  2. 搭建可以转换office软件的服务, 或者直接在项目服务器上直接搭建转换服务
  3. 使用soffice命令转换office文件为pdf文件
  4. 更改header头,输出pdf文件内容

实现步骤

  1. 安装openoffice或liboffice, 有的系统已经预装该软件,使用soffice --help测试是否安装,如果没有安装,使用以下命令安装
    			centos: yum install libreoffice
    			ubuntu: apt-get install libreoffice
    
  2. 编写php脚本, 这样使用tp3.2搭建的转换服务
        public function viewFile()
     {
         define('APPLICATION_PATH', dirname(dirname(__DIR__)));
         define('FILE_UPLOAD_PATH', APPLICATION_PATH . '/Runtime/Uploadfile/');
         define('FILE_CONVERT_PATH', APPLICATION_PATH . '/Runtime/Convertfile/');
         $fileUrl = I('file_url');
         if (empty($fileUrl)) {
             return $this->ajaxReturn(array('status' => 'failed', 'message' => 'parameters is missing!'));
         }
         $urlInfo = pathinfo($fileUrl);
         $httpUrlInfo = parse_url($fileUrl);
         switch ($urlInfo['extension']) {
             case 'doc':
                 $result = $this->getConvertViewFile($fileUrl, $httpUrlInfo, $urlInfo);
                 if ($result['status'] == 'failed') {
                     return $this->ajaxReturn($result);
                 } else {
                     header('Content-type:application/pdf');
                     echo $result['message'];
                 }
                 exit();
             case 'pdf':
                 $result = $this->getViewFile($fileUrl, $httpUrlInfo);
                 if ($result['status'] == 'failed') {
                     return $this->ajaxReturn($result);
                 } else {
                     header('Content-type:application/pdf');
                     echo $result['message'];
                 }
                 exit();
             case 'txt':
                 $result = $this->getViewFile($fileUrl, $httpUrlInfo);
                 if ($result['status'] == 'failed') {
                     return $this->ajaxReturn($result);
                 } else {
                     header('Content-type:text/plain');
                     echo $result['message'];
                 }
                 exit();
             default:
                 return $this->ajaxReturn(array('status' => 'failed', 'message' => '不合法的文件格式!'));
         }
         return $this->ajaxReturn(array('status' => 'failed', 'message' => 'url不合法!'));
     }
     private function getConvertViewFile($fileUrl, $httpUrlInfo, $urlInfo)
     {
         $result = $this->getViewFile($fileUrl, $httpUrlInfo);
         if ($result['status'] == 'failed') {
             return $result;
         }
         $fileKey = md5($result['message']);
         $convertFile = FILE_CONVERT_PATH . $fileKey . '.pdf';
         // 获取缓存文件
         if (file_exists($convertFile)) {
             $fileData = file_get_contents(FILE_CONVERT_PATH . $fileKey . '.pdf');
             return array('status' => 'success', 'message' => $fileData);
         } else {
             // 保存文件
             $day = date('Y-m-d', time());
             $path = FILE_UPLOAD_PATH . $day . '/';
             is_dir($path) OR mkdir($path, 0777, true);
             $originFileUrl = $path . $fileKey . '.' . $urlInfo['extension'];
             if (!file_exists($originFileUrl)) {
                 $end = file_put_contents($originFileUrl, $result['message']);
                 if ($end === false) {
                     return array('status' => 'failed', 'message' => '文件保存失败!');
                 }
             }
    
             // 转换文件
             $isSuccess = $this->convertFile2Pdf($originFileUrl);
             if (!$isSuccess) {
                 return array('status' => 'failed', 'message' => '文件转换失败!');
             }
    
             // 再次获取文件
             if (file_exists($convertFile)) {
                 $fileData = file_get_contents($convertFile);
                 return array('status' => 'success', 'message' => $fileData);
             } else {
                 return array('status' => 'failed', 'message' => '文件处理失败!');
             }
         }
     }
    
     private function convertFile2Pdf($originFileUrl)
     {
         try {
             $shellPath = APPLICATION_PATH . '/Runtime/shell/';
             $shellName = 'new_convert.sh';
             $convertFile = FILE_CONVERT_PATH;
             $execute = $shellPath . $shellName . " {$originFileUrl} {$convertFile} ";
             $execute = 'bash ' . $execute . ">> " . $shellPath . "newconvert_log 2>&1";
             // 阻塞转换文件
             system($execute, $status);
             if ($status === 0) {
                 return true;
             } else {
                 return false;
             }
         } catch (\Exception $e) {
             return false;
         }
     }
    
     private function getViewFile($fileUrl, $httpUrlInfo)
     {
         try {
             if (empty($httpUrlInfo['scheme'])) {
                 $url = 'http://' . $fileUrl;
                 $fileData = file_get_contents($url);
                 if (empty($fileData)) {
                     $url = 'https://' . $fileUrl;
                     $fileData = file_get_contents($url);
                 }
             } else {
                 $fileData = file_get_contents($fileUrl);
             }
             if (empty($fileData)) {
                 return array('status' => 'failed', 'message' => '文件找不到!');
             } else {
                 return array('status' => 'success', 'message' => $fileData);
             }
         } catch (\Exception $e) {
             return array('status' => 'failed', 'message' => $e->getMessage());
         }
     }
    

3.bash转换脚本

#!/bin/bash
startTime=$(date "+%Y-%m-%d %H:%M:%S")
echo "---------------------------------startTime: ${startTime}------------------------------------------------"
echo "sourceFile: ${1}"
echo "outFile: ${2}"
echo "----------------------------------------------start--------------------------------------------------"
soffice --headless --invisible --convert-to pdf:writer_pdf_Export ${1} --outdir ${2} "-env:UserInstallation=file:///tmp/LibreOffice_Conversion_${USER}"
echo "----------------------------------------------end----------------------------------------------------"
endTime=$(date "+%Y-%m-%d %H:%M:%S")
echo "---------------------------------endTime: ${endTime}------------------------------------------------"

  1. 访问转换服务
    转换服务域名/home/index/viewFile?file_url=你的文件网址

遇到的问题

一。运行bash脚本报错如下
[Java framework] Error in function createSettingsDocument (elements.cxx).
javaldx failed!

解决办法: 查看/etc/passwd中的apache用户, passwd的apache默认是

   	 apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin 

可以发现apache的用户目录是/usr/share/httpd,使用一下命令更改/usr/share/httpd权限

	chown -R apache:apache /usr/share/httpd

这样就可以解决上述问题

二。转换文件时中文乱码的问题,一般中文会变成方框

一般在linux服务器上运行soffice命令时,会在用户目录下面的隐藏目录.config中产生libreoffice配置文件,例如你登录的用户是user, 用户目录是/home/user, 这个路径为/home/user/.config/libreoffice,root用户是/root/.config/libreoffice,解决上述问题,解决办法有两种:

//将C:\Windows\Fonts下的宋体,即simsun.ttc(我为了防止意外,将Fonts目录下所以文件复制过去了,你随意)复制 linux机器的/usr/share/fonts中, 更改文件权限为,
sudo chmod 644 simsun.ttc
//更新字体缓存:
sudo fc-cache -fv

上述方法这样会破坏系统的默认字体设置,特别是Ubuntu,由于宋体的优先级高于文泉驿,系统会优先抓取宋体,默认漂亮的光滑矢量字体会变成点矩阵的宋体,因为上述更改是将所有用户的字体设置进行了更改
第二种解决办法:
刚才我们在上面说了。在执行soffice文件,会在用户目录下产生libreoffice缓存配置文件, 我们只需要将上面的window下的文字文件复制到用户的libreoffice文件中即可,步骤如下

    1. cat  /etc/passwd  查看apache用户目录为/usr/share/httpd, 但是apache是不可登录用户, 默认如下
			apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
    2. 暂时修改为可登陆用户,如下
	       apache:x:48:48:Apache:/usr/share/httpd:/bin/bash
    3. 切换apache用户
			su apache
    4. 在apache随便一个文件下执行soffice命令,下面的源文件,你自己去创建或者上传到服务器一个即可
	       soffice --convert-to pdf  xx.doc(源文件) --outdir  输出文件目录
    5. 这样在/usr/share/httpd下就会有个.config文件,里面会有libreoffice目录, 进入/usr/share/httpd/.config/libreoffice/4/user, 在该目录下创建fonts目录, 将C:\Windows\Fonts下的文件复制到该目录中
    6. 更改文件权限
			chown -R apache:apache /usr/share/httpd
    7. 退出apache用户,然后将其改为不可登录用户
    		exit
    		apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin     

上述方法只会对apache用户的字体进行更改,不会影响其他用户

二。执行bash脚本时,命令无响应,无返回

原因: 这是因为soffice命令在一个用户时只能启动一个进程,
解决办法:1. 先查看服务器进程,将正在转换soffice命令杀死,如下
在这里插入图片描述
先使用kill -9 进程号, 将上述程序全部杀死,然后在执行soffice命令时添加"-env:UserInstallation=file:///tmp/LibreOffice_Conversion_${USER}",每次执行创建临时用户即可, 在上面的步骤脚本时已经添加了该参数
解决办法2. 上面是创建临时用户,还可以使用创建临时目录,脚本如下:

#!/bin/sh
dir=`mktemp -d`
if [ -d $dir ]; then
  HOME=$dir
  export HOME
  libreoffice $@
  rm -Rf $dir
fi
php脚本:
exec("/usr/bin/libreoffice_run_many --headless --invisible --convert-to pdf ./general.pptx 2>&1", $output, $return);
print_r($output);

你可能感兴趣的:(PHP起步)