服務端自動將上傳的 PDF 文檔第一頁(或指定頁)轉為 PNG 縮略圖,用於列表預覽、文檔管理系統等場景。
apt-get install imagemagick libmagickwand-dev (Ubuntu/Debian)pecl install imagick,啟用 extension=imagick.sopolicy.xml 允許讀取 PDF(默認近期版本限制 PDF 讀取):/etc/ImageMagick-6/policy.xml 或 /etc/ImageMagick-7/policy.xml 中修改:<policy domain="coder" rights="read|write" pattern="PDF" /><?php
/**
* PDF 指定頁轉 PNG 縮略圖
* @param string $pdfPath PDF 文件絕對路徑
* @param string $saveDir 輸出目錄(自動創建)
* @param int $page 頁碼(0-based:0=第一頁,-1=所有頁)
* @param int $thumbWidth 縮略圖寬度
* @param int $thumbHeight 縮略圖高度
* @param int $resolution 渲染分辨率 DPI(越高越清晰,越慢)
* @param int $quality JPEG/PNG 質量 1-100
* @return string|array|false 成功返回文件路徑(單頁)或路徑數組(多頁),失敗 false
*/
function pdfToPngThumb(
string $pdfPath,
string $saveDir,
int $page = 0,
int $thumbWidth = 200,
int $thumbHeight = 150,
int $resolution = 150,
int $quality = 85
) {
if (!extension_loaded('imagick')) {
error_log('Imagick 擴展未加載');
return false;
}
if (!file_exists($pdfPath)) {
error_log("PDF 文件不存在: $pdfPath");
return false;
}
if (!is_dir($saveDir) && !mkdir($saveDir, 0755, true)) {
error_log("無法創建目錄: $saveDir");
return false;
}
try {
$im = new Imagick();
$im->setResolution($resolution, $resolution); // 必須在 readImage 前設置
$im->setCompressionQuality($quality);
// 讀取指定頁:$pdfPath"[0]" 表示第一頁
$im->readImage($pdfPath . "[$page]");
// 縮放:保持比例,使用最佳濾波器
$im->thumbnailImage($thumbWidth, $thumbHeight, true, true);
// 設置輸出格式為 PNG
$im->setImageFormat('png');
// 生成唯一文件名
$baseName = pathinfo($pdfPath, PATHINFO_FILENAME);
$timestamp = time();
$rand = mt_rand(1000, 9999);
if ($page === -1) {
// 所有頁:每頁生成一個文件
$paths = [];
foreach ($im as $i => $frame) {
$frame->setImageFormat('png');
$fileName = "{$baseName}_page{$i}_{$timestamp}_{$rand}.png";
$filePath = $saveDir . DIRECTORY_SEPARATOR . $fileName;
if ($frame->writeImage($filePath)) {
$paths[] = $filePath;
}
}
return $paths ?: false;
} else {
// 單頁
$fileName = "{$baseName}_thumb_{$timestamp}_{$rand}.png";
$filePath = $saveDir . DIRECTORY_SEPARATOR . $fileName;
if ($im->writeImage($filePath)) {
return $filePath;
}
return false;
}
} catch (ImagickException $e) {
error_log("PDF 轉圖失敗: " . $e->getMessage());
return false;
} finally {
if (isset($im)) {
$im->clear();
$im->destroy();
}
}
}
// 使用示例
$pdfFile = '/var/www/uploads/doc.pdf';
$thumbDir = '/var/www/public/thumbs';
$result = pdfToPngThumb($pdfFile, $thumbDir, 0, 300, 200);
if ($result) {
echo "<img src='/thumbs/" . basename($result) . "' alt='PDF縮略圖'>";
} else {
echo "縮略圖生成失敗";
}
| 參數 | 推薦值 | 影響 |
|---|---|---|
| resolution (DPI) | 150-300 | 越高文字越銳利,但內存/CPU 指數級增長。列表縮略圖 150 足夠。 |
| thumbnailImage | true, true | 第三參數 bestfit 保持比例,第四參數 legacy 兼容舊行為。 |
| setCompressionQuality | 80-90 | PNG 無損壓縮,質量影響文件大小與壓縮時間。 |
| colorspace | $im->setColorspace(Imagick::COLORSPACE_sRGB) | 確保輸出 sRGB,避免 CMYK 導致顏色偏差。 |
policy.xml PDF 讀取策略,重載 PHP-FPM。ini_set('memory_limit', '512M') 或降低 DPI。ghostscript 包。$im->setBackgroundColor(new ImagickPixel('white')) 並 $im->flattenImages()。readImage($pdf."[0]") 語法正確,Imagick 會自動選擇幀。| 方案 | 優點 | 缺點 |
|---|---|---|
| Imagick + Ghostscript | 功能全、格式支持廣、PHP 原生擴展 | 依賴重、配置複雜、內存佔用高 |
| pdftoppm (poppler-utils) | 極快、輕量、專門針對 PDF 渲染 | 需 exec() 調用、輸出格式固定 |
| pdf.js (前端) | 零後端成本、用戶端渲染 | 首屏慢、移動端性能差、無法服務端預覽 |
| 第三方 API (CloudConvert 等) | 無維護、高可用 | 成本、隱私、延遲、依賴外網 |
高併發場景建議:隊列異步處理(Redis/RabbitMQ + Worker),或使用 pdftoppm 通過 proc_open() 非阻塞調用。