PHP:通过PHP读取并呈现PDF文件以下载,在某些情况下会生成损坏的文件


PHP: read and present a PDF file to download via PHP is generating corrupted files in some cases

我使用下面的代码来读取和呈现一个PDF文件给用户:

$file='file.pdf';
$filepath="/path-to-download-folder/$file";
if(!is_file($filepath)) die('Ops!');
$f=fopen($filepath,'r');
if($f){
    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Content-Type: '.mime_content_type($filepath));
    header('Content-Length: '.filesize($filepath));
    header('Content-Disposition: attachment; filename="'.$file.'"');
    fpassthru($f);
}else die("Ops!");
fclose($f);

然而,有些人报告说,当他们试图打开PDF文件时,它是损坏的。

我做错了什么或忘记一些重要的标题?有更好的方法来达到同样的效果吗?

更新:

我通过电子邮件发送了PDF文件,用户可以打开文件。所以一定是PHP和Nginx处理文件的方式有问题。

所以我在Nginx上关闭了gzip,但错误仍然存在。

我也应用了下面的提示,所以代码现在是这样的:

$file='file.pdf';
$filepath="/path-to-download-folder/$file";
if(!file_exists($filepath)){
  header('HTTP/1.1 404 Not Found');
  exit;
}elseif(!is_file($filepath) or !is_readable($filepath)){
  header('HTTP/1.1 403 Forbidden');
  exit;
}else{
    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Content-Type: '.mime_content_type($filepath));
    header('Content-Length: '.filesize($filepath));
    header('Content-Disposition: attachment; filename="'.$file.'"');
    set_time_limit(0); // Big files/slow connections may result in incomplete downloads
    readfile($filepath);
    die;
}

但是我仍然收到这个错误:http://mlkshk.com/r/8FGS

更新:

我在文件中做了一个区别:

compare -verbose -debug coder Dicas1.pdf Dicas1A.pdf -compose src OUT.tmp

输出如下:

"gs" -q -dQUIET -dPARANOIDSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pnmraw" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r72x72"  "-sOutputFile=/tmp/magick-XXuCqreY" "-f/tmp/magick-XXNkQbcr" "-f/tmp/magick-XXoICX9T"
   **** Warning: File has some garbage before %PDF- .
   **** Error: Cannot find a %%EOF marker anywhere in the file.
   **** Warning:  An error occurred while reading an XREF table.
   **** The file has been damaged.  This may have been caused
   **** by a problem while converting or transfering the file.
   **** Ghostscript will attempt to recover the data.

我在Leafpad中打开:

原始文件:

%PDF-1.5
%Çì¢
1 0 obj
<<
/Type /Catalog
/Outlines 3 0 R
/Pages 4 0 R
/Dests 5 0 R
/AcroForm 6 0 R
/Names 7 0 R
/Threads 8 0 R
/PageLayout /SinglePage
/ViewerPreferences
<<
/PageDirection /L2R
 >>
>>
endobj
2 0 obj
<<
/Creator (Scribus 1.5.0.svn)
/Producer (Scribus PDF Library 1.5.0.svn)
/Title <>
/Author <>
/Subject <>
/Keywords <>
/CreationDate (D:20111016162546Z)
/ModDate (D:20111016162546Z)
/Trapped /False
>>
endobj
9 0 obj
<<
/Length 154566
/Length1 275572
/Filter /FlateDecode
>>
stream

PHP和Nginx服务的文件:

6Wm931Ja.G46X5WID+1K9G93F.3FD.2IXCWm<br>%PDF-1.5
%Çì¢
1 0 obj
<<
/Type /Catalog
/Outlines 3 0 R
/Pages 4 0 R
/Dests 5 0 R
/AcroForm 6 0 R
/Names 7 0 R
/Threads 8 0 R
/PageLayout /SinglePage
/ViewerPreferences
<<
/PageDirection /L2R
 >>
>>
endobj
2 0 obj
<<
/Creator (Scribus 1.5.0.svn)
/Producer (Scribus PDF Library 1.5.0.svn)
/Title <>
/Author <>
/Subject <>
/Keywords <>
/CreationDate (D:20111016162546Z)
/ModDate (D:20111016162546Z)
/Trapped /False
>>
endobj
9 0 obj
<<
/Length 154566
/Length1 275572
/Filter /FlateDecode
>>
stream

这是PHP放在文件开头的垃圾:

6Wm931Ja.G46X5WID+1K9G93F.3FD.2IXCWm<br>

我发现会话值在代码的另一部分被回显,这是从一开始就是真正的问题。

谢谢。

到目前为止,这种问题最常见的原因是<?php ?>标记前后的前导/尾随空白。请注意,您可能不需要?>标记,这有助于避免此问题。

第二个最常见的原因是在输出文件后忘记调用exit/die

请检查以上两点。

编辑

下面是我写代码的方法:
$file = 'file.pdf';
$filepath = "/path-to-download-folder/$file";
if (!file_exists($filepath)) {
  header('HTTP/1.1 404 Not Found');
  exit;
} else if (!is_file($filepath) || !is_readable($filepath)) {
  header('HTTP/1.1 403 Forbidden');
  exit;
}
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Content-Type: '.mime_content_type($filepath));
header('Content-Length: '.filesize($filepath));
header('Content-Disposition: attachment; filename="'.$file.'"');
set_time_limit(0); // Big files/slow connections may result in incomplete downloads
readfile($filepath);
exit;

另外,您应该以二进制模式打开PDF文件:

$f = fopen($filepath, 'rb');

(或者你可以直接用readfile())