7

I've got a problem that has risen many times on SO, but I can't seem to find the solution to mine! I'm trying to deliver a pdf file to the client without it opening in the browser, the file downloads but it is corrupt when I open it and is missing quite a few bytes from the original file. I've tried several such methods for downloading the file but I'll just show you the latest I've used and hopefully get some feedback.

I have also opened the downloaded PDF in a text editor and there are no php errors at the top of it that I can see!

I'm also aware that readfile() is much quicker but for testing purposes I am desperate to get anything working so I used the while(!feof()) approach!

Anyway enough rambling, heres the code (taken from why my downloaded file is alwayes damaged or corrupted?):

$file     = __DIR__ . '/reports/somepdf.pdf';
$basename = basename($file);
$length   = sprintf("%u", filesize($file));

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $basename . '"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $length);

ob_clean();
set_time_limit(0);
readfile($file);

Also to note was the difference in file size:

Original: 351,873 bytes
Downloaded: 329,163 bytes
Community
  • 1
  • 1
David C
  • 664
  • 1
  • 8
  • 21
  • 1
    Did you try `readfile()`? – barbashov Apr 08 '13 at 21:48
  • @DavidC799: If you want to discuss the answer of a previous questions, please leave a comment there. Not just drop in here some code and tell us "it does not work". Keep in mind, only somebody else accepted that answer, it must not mean that the code must work for you, too. And for testing purposes, please reduce the code to the bare minimum to provoke the problem. E.g. no function, just a hardcoded filename. Use readfile. – hakre Apr 08 '13 at 21:51
  • @barbashov yes I have tried several different methods. – David C Apr 08 '13 at 21:53
  • @DavidC799 maybe changing `Content-type` to `application/pdf` will help? BTW, what's the value of `Content-length` returned in headers? – barbashov Apr 08 '13 at 21:56

3 Answers3

8

Make sure you're not running any compression output buffering handlers, such as ob_gzhandler. I had a similar case and I had to disable output buffering for this to work properly

periklis
  • 10,102
  • 6
  • 60
  • 68
  • Indeed indeed I am! Now that you mention it I did see someone else mention that in another post. Will try a few things and let you know how it goes. Sorry for the hassle! – David C Apr 08 '13 at 21:55
  • Success! Many thanks for your help, always something so simple :( – David C Apr 08 '13 at 21:57
  • @DavidC799: Well, just read the other questions, most answers on the website already you only need to find them ;) – hakre Apr 08 '13 at 21:57
  • @hakre Yea, sorry for wasting your time heh! – David C Apr 08 '13 at 22:01
  • @DavidC799: I don't think you wasted anyone's time, it was a valid, not obvious question, nicely put imho. – periklis Apr 08 '13 at 22:03
  • @periklis: You miss to explain in your answer why technically this is a problem (or must be a problem at all). Can you explain why it is not possible to use ob_gzhandler and the output? I so no reason why the gz_handler should be disabled. – hakre Apr 08 '13 at 22:06
  • @DavidC799: It's not wasting any time, it's about finding out what the correct answer is: http://stackoverflow.com/a/15889925/367456 – hakre Apr 08 '13 at 22:11
7

You are using the the ob_gzhandler on the output buffer.

It works by gzencoding chunks of output. The output then is a stream of the encoded chunks.

Each chunk needs to get some bytes to get encoded, so the output is a little bit buffered until enough bytes are available.

However at the end of your script you discard the remaining buffer instead of flushing it.

Use ob_end_flush() instead of ob_clean() and the file gets through fully and not corrupted.

You are to use the transfer encoding of ob_gzhandler with file-uploads not having any problems when you don't destroy the output-buffer before it could have done it's work.

This is also the same if any other output buffering that works chunked would have been enabled.

Example code:

$file     = __DIR__ . '/somepdf.pdf';
$basename = basename($file);
$length   = sprintf("%u", filesize($file));

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $basename . '"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $length);

ob_end_flush();   // <--- instead of ob_clean()

set_time_limit(0);
readfile($file);

return;

(FYI: actually even the ob_end_flush(); is not necessary, the important part is not to just kick the output-buffer before it could have done it's work)

hakre
  • 193,403
  • 52
  • 435
  • 836
  • I can confirm that hakre's code does work as expected; thank you for the detailed explanation – periklis Apr 08 '13 at 22:29
  • Also I've just checked readfile() source in PHP and you should (if you can) always use it instead of doing own the fopen/buffered read as in the question. `readfile()` is far superior for file-download outputs because it can make use of benefits in the SAPI that are not possible in userland code. – hakre Apr 08 '13 at 22:36
-1

I fought with using content-disposition for pushing a PDF download for two days before finding a solution to my problem. My PDF files were also smaller in size and corrupt - however, I could open them in Windows Preview - just not Adobe. After much troubleshooting, I discovered that Adobe expects the %PDF in the first 1024 bytes of the file. I was doing all my file type checks in my php code before creating the headers. I took out the majority of code before the headers and my PDF file was fixed.

You might not be setting it up the same way I did, but it might be the same problem:

http://helpx.adobe.com/acrobat/kb/pdf-error-1015-11001-update.html