
一、方案背景与优势
在企业级应用中,将Word文档转换为PDF是一项常见需求。相比于在Windows下使用COM组件调用Office,在Linux服务器上使用PHP配合LibreOffice具有更好的稳定性和跨平台能力。LibreOffice开源免费,且支持无头模式(Headless)运行,非常适合服务端批量处理文档,避免了版权问题和Windows服务器的资源开销。
二、环境准备
首先需要在服务器上安装LibreOffice核心程序。以CentOS和Ubuntu为例:
# CentOS yum install libreoffice # Ubuntu apt-get install libreoffice
安装完成后,可以通过命令行验证是否安装成功:
libreoffice --version
三、基础转换逻辑与并发核心问题
LibreOffice提供了命令行转换方式,基础命令为:
libreoffice --headless --convert-to pdf 源文件.docx --outdir 输出目录
在PHP中,我们通常使用exec()或shell_exec()来执行此命令。但在实际生产环境中,如果直接使用基础命令,高并发时会出现转换失败或进程卡死的问题。这是因为LibreOffice默认会在用户目录下创建锁文件,当多个进程同时启动时会发生冲突。
解决方案:为每次转换指定独立的用户配置目录(UserInstallation),通过添加-env:UserInstallation=file:///临时目录/唯一ID参数,可以实现多进程并行互不干扰,这是实现高效转换的核心。
四、完整PHP实现代码
下面是一个封装好的转换类,包含了并发安全处理、临时目录自动清理以及异常捕获机制:
<?php
class LibreOfficeConverter
{
private $libreOfficeBin = 'libreoffice';
/**
* 将Word文档转换为PDF
* @param string $sourcePath 源文件绝对路径
* @param string $outputDir 输出目录绝对路径
* @return string|false 成功返回PDF文件路径,失败返回false
*/
public function convertToPdf($sourcePath, $outputDir)
{
if (!file_exists($sourcePath)) {
throw new Exception("源文件不存在: " . $sourcePath);
}
$uniqid = uniqid('lo_');
$tempProfileDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $uniqid;
mkdir($tempProfileDir, 0777, true);
// 构建并发安全的命令,指定独立的用户配置目录
$command = sprintf(
'%s --headless --convert-to pdf -env:UserInstallation=file:///%s %s --outdir %s 2>&1',
$this->libreOfficeBin,
$tempProfileDir,
escapeshellarg($sourcePath),
escapeshellarg($outputDir)
);
exec($command, $output, $returnVar);
// 执行完毕后清理临时用户配置目录,防止临时文件堆积
$this->removeDirectory($tempProfileDir);
if ($returnVar !== 0) {
throw new Exception("LibreOffice转换失败: " . implode("n", $output));
}
$pdfFileName = pathinfo($sourcePath, PATHINFO_FILENAME) . '.pdf';
$pdfFilePath = rtrim($outputDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $pdfFileName;
return file_exists($pdfFilePath) ? $pdfFilePath : false;
}
/**
* 递归删除目录
*/
private function removeDirectory($dir)
{
if (!is_dir($dir)) {
return;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . DIRECTORY_SEPARATOR . $file;
is_dir($path) ? $this->removeDirectory($path) : unlink($path);
}
rmdir($dir);
}
}
// 使用示例
try {
$converter = new LibreOfficeConverter();
$source = '/var/www/html/uploads/demo.docx';
$outDir = '/var/www/html/pdf/';
$pdfPath = $converter->convertToPdf($source, $outDir);
echo "转换成功,PDF路径: " . $pdfPath;
} catch (Exception $e) {
echo "转换异常: " . $e->getMessage();
}
?>五、常见问题与优化建议
1. 中文乱码或方块字:LibreOffice在Linux下可能缺少中文字体。将Windows下的字体(如SimSun, Microsoft YaHei)复制到Linux的/usr/share/fonts/目录下,然后执行fc-cache -fv刷新字体缓存即可。
2. 权限问题:确保运行PHP进程的用户(如www-data)对源文件、输出目录以及系统临时目录有读写和执行权限。
3. 转换超时:如果文档非常大,默认的30秒PHP执行时间可能不够。建议在脚本中动态调整超时限制set_time_limit(0),或在命令行中使用timeout控制最大执行时间,防止进程僵尸。
4. 接口封装:如果需要对外提供接口服务,可以参考 www.ipipp.com 提供的API设计规范,将上述类封装为RESTful API,结合消息队列实现异步转换与状态回调,进一步提升系统吞吐量。