使用PHP exec()与GhostScript合并PDF时错误码1的原因及解决方法
解决GhostScript合并PDF在生产服务器返回状态码1的问题
咱们先理清楚:GhostScript返回状态码1一般是一般性执行错误,不是某个特定问题,得从权限、路径、命令限制、文件本身这几个方向逐一排查,结合你的场景,我给你一步步分析:
1. 先搞定权限和sudo的问题
你本地用了sudo,但生产服务器里PHP是跑在Apache的系统用户(比如www-data)下的,直接用sudo可能踩两个坑:
- 要么这个用户没有sudo权限,导致命令直接失败;
- 就算配置了sudo,也可能因为环境变量不一致(比如PATH不同)出问题。
解决步骤:
- 先去掉命令里的
sudo,直接执行gs命令试试; - 手动切换到Apache用户身份测试命令:
su www-data -c "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile=/绝对路径/files/ovmext/sample.pdf /绝对路径/files/wkhtmltopdf/pdf-sample-*.pdf" - 检查输出目录
files/ovmext/和源PDF目录的权限,确保www-data有读(源文件)和写(输出文件)的权限。
2. 排查路径问题
相对路径在不同环境下很容易踩坑:
- 本地的
files/目录相对于脚本的位置,可能和生产服务器不一样; - GhostScript的路径可能不在Apache用户的PATH里(不过你说单独执行
gs -v正常,这个可能性低,但可以试试)。
解决步骤:
- 把所有路径改成绝对路径,比如用
realpath()函数获取文件的绝对路径:$outputPath = realpath('./files/ovmext/') . '/sample.pdf'; $sourcePath = realpath('./files/wkhtmltopdf/') . '/pdf-sample-*.pdf'; $command = "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile=$outputPath $sourcePath"; - 用
which gs命令在服务器上查gs的绝对路径,比如/usr/bin/gs,然后把命令里的gs换成这个绝对路径。
3. 重点排查:命令行长度限制!
这大概率是你遇到的核心问题:Linux系统有个ARG_MAX参数,限制了命令行参数的总长度(默认一般是128KB左右),5000个PDF文件名加起来肯定超过这个限制了!本地Mac的ARG_MAX值更大(比如默认是2MB),所以没问题,但生产服务器触发了这个限制,导致命令执行失败返回状态码1。
完美解决办法:用GhostScript的@文件名语法,把所有要合并的PDF路径写到一个临时文件里,让gs从文件里读取列表:
// 1. 创建临时文件存储PDF列表 $tempFile = tempnam(sys_get_temp_dir(), 'pdf_merge_list'); $handle = fopen($tempFile, 'w'); // 2. 获取所有源PDF的绝对路径,逐行写入临时文件 $pdfFiles = glob(realpath('./files/wkhtmltopdf/') . '/pdf-sample-*.pdf'); foreach ($pdfFiles as $file) { fwrite($handle, $file . "\n"); } fclose($handle); // 3. 执行GhostScript命令,用@临时文件读取文件列表 $outputPath = realpath('./files/ovmext/') . '/sample.pdf'; $command = "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile=$outputPath @$tempFile"; exec($command, $array, $return_var); // 4. 执行完成后删除临时文件 unlink($tempFile); // 检查结果 if ($return_var == 0) { echo 'works! - statuscode: ' . $return_var; } else { echo 'does not work! - statuscode: ' . $return_var; // 可以获取详细错误信息 $errorOutput = shell_exec($command . ' 2>&1'); var_dump($errorOutput); }
4. 查看详细错误日志
去掉命令里的-q参数,让GhostScript输出详细错误,同时把标准错误重定向到标准输出,这样能看到具体哪里出错了:
$command = 'gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile=files/ovmext/sample.pdf files/wkhtmltopdf/pdf-sample-*.pdf'; $fullOutput = shell_exec($command . ' 2>&1'); echo $fullOutput;
这能帮你快速定位是某个PDF文件损坏,还是权限问题,或者资源不足。
5. 资源限制检查
合并5000个PDF需要不少内存和CPU,你可以检查:
- PHP的
memory_limit配置,在脚本开头临时调高试试:ini_set('memory_limit', '512M'); - 服务器的ulimit限制,比如用
ulimit -a查看,看进程的内存、打开文件数限制是不是不够。
内容的提问来源于stack exchange,提问作者Michał Płonski
相关产品推荐
相关产品推荐

