1. 问题

实现一个功能:导出pdf。
网上有很多种办法来导出pdf,但是我选择了wkhtmltopdf。wkhtmltopdf是需要安装在操作系统的软件,导出效率可观。
系统现状:

  1. 项目运行在centos操作系统
  2. php环境

    2. 处理过程

  1. 思路:在centos安装wkhtmltopdf然后通过php调用命令来处理。

但是发现,系统环境原因,不能调用操作系统的wkhtmltopdf来导出,这就是本次处理最大的问题所在。只能异步处理,写个shell脚本来调用,在用php脚本来检测是否成功。
image.png

  1. 实现:
    1. 定时脚本1:php脚本,检测到有需要导出pdf的任务的时候,通过smarty来生成静态html,存到目录A。
    2. 定时脚本2:shell脚本,检测到目录A有html时,将html生成为pdf,存到目录B,并删除目录A的html。
    3. 定时脚本3:php脚本,检测到目录B有文件时,将该文件转存到目录C,并删除目录B原来的pdf。

简易代码如下:

  1. # 脚本1
  2. <?php
  3. ob_start();
  4. $smarty = new \package\template\Smarty($config);
  5. $smarty->assign('data', $r);
  6. $smarty->show($template_dir.'template_index.html',false);
  7. $content = ob_get_clean();
  8. $template_dir = $main_data . 'report/project/html/';
  9. $html_file = $template_dir.$id.'_'.$data_id.'.html';
  10. file_put_contents($html_file,$content);
  1. # 脚本2
  2. for file in $(ls | grep "^[0-9]*_[0-9]*.html$")
  3. do
  4. pdf_name="${file%%.*}"
  5. pdf_header="${pdf_name}header.html"
  6. pdf_file_path="${pdf_path}${pdf_name}.pdf"
  7. `/usr/local/bin/wkhtmltopdf --header-html "${pdf_header}" --footer-html tmplate_footer.html "${file}" "${pdf_file_path}"`
  8. # 判断文件是否生成成功,是则删除html
  9. if [ -f "${pdf_file_path}" ];then
  10. rm "${file}"
  11. rm "${pdf_header}"
  12. fi
  13. done
# 脚本3
<?
$data = scandir($pre_pdf_dir);
foreach ($data as $f) {

}
  1. 调试:

出现一个问题:一直推送同一个pdf给人员,而且该pdf乱码。调试发现,因为脚本2没有把html删除,但是为什么没有删除呢?
原因:脚本2在导出大文件的时候,先生成文件名,所以当脚本3检测到目录B有文件生成,就把文件推送出去了,并且删除掉原文件,导致脚本2发现导出pdf失败了,就没有删除掉html,然后就无限循环,这就是问题的整个过程。
解决:

  1. 脚本3检测文件时识别后缀为非pdf时不处理。
  2. 在运行脚本2的生成过程中,将要生成的文件后缀改成非pdf,等生成完成后再改回pdf后缀。

    # 脚本2
    for file in $(ls | grep "^[0-9]*_[0-9]*.html$")
    do
    pdf_name="${file%%.*}"
    pdf_header="${pdf_name}header.html"
    pdf_file_path="${pdf_path}${pdf_name}.pdf"
    
    # 临时文件
    pdf_tmp="${pdf_file_path}.tmp"
    
    `/usr/local/bin/wkhtmltopdf --header-html "${pdf_header}" --footer-html tmplate_footer.html "${file}" "${pdf_tmp}"`
    
    # 判断文件是否生成成功,则文件改名,删除html
    if [ -f "${pdf_tmp}" ];then
      # 临时文件改名为pdf
      mv "${pdf_tmp}" "${pdf_file_path}"
    
      # 删除html
      rm "${file}"
      rm "${pdf_header}"
    fi
    done
    
    # 脚本3
    <?
    $data = scandir($pre_pdf_dir);
    foreach ($data as $f) {
      $info = pathinfo($f);
    
    // 判断是否生成完成pdf,如果后缀不是pdf则文件未生成完成
    if ($info['extension'] != 'pdf') continue;
    }
    
  1. 调试,测试,完成,nice

    3. 总结

  • 由于程序设计时没有做更多的可靠性处理,导致一个环节出错,就可能出现不可预料的错误。此程序有改进空间,比如记录推送次数,推送失败就做出进一步的处理。