PDF download got garbage

Hi experts,

The PDF download is not working. It is supposed to let user to download & save to disc, but the book content was displayed in the page as garbage, here is the code:

$book = mysql_fetch_array( $books );
$bookName = $book[ ‘name’ ];
$mimeType = $book[ ‘mimetype’ ];
$bookContent = $book[ ‘content’ ];
$bookSize = strlen( $bookContent );
$disposition = ‘attachment’;
echo “<p>bookName=$bookName,mimeType=$mimeType,booksize=$bookSize</p>”;

if( strpos( $_SERVER[ ‘HTTP_USER_AGENT’ ], ‘MSIE 5’ ) ||
strpos( $_SERVER[ ‘HTTP_USER_AGENT’ ], ‘Opera 7’ ) )
{
$mimeType = ‘application/x-download’;
}

header( “content-disposition:$disposition; filename=$bookName” );
header( “content-type:$mimeType” );
header( “content-length:$bookSize” );

echo $bookContent;

test link:
http://books.chnmates.com/delivery.php?tranId=151127125

Can you help?
Thanks a lot.

You should not sent any other data than the headers and the content of the book.

Before your headers you got a echo that show some information about the book, remove that one.

In addition you should have more headers, for example that its a binary download, that the browser should not cache it etc.

$bookContent = $book[ ‘content’ ];
$bookSize = strlen( $bookContent );
$disposition = ‘attachment’;
//echo “<p>bookName=$bookName,mimeType=$mimeType,booksize=$bookSize</p>”;

if( strpos( $_SERVER[ ‘HTTP_USER_AGENT’ ], ‘MSIE 5’ ) ||
strpos( $_SERVER[ ‘HTTP_USER_AGENT’ ], ‘Opera 7’ ) )
{
$mimeType = ‘application/x-download’;
}

header( “content-disposition:$disposition; filename=$bookName” );
header( “content-type:$mimeType” );
header( “content-length:$bookSize” );
header(“Cache-Control: no-cache, must-revalidate”);
header(‘Content-Transfer-Encoding: binary’);

echo $bookContent;

// update the download record
$ships = $ships + 1;
$sql = “UPDATE transactions SET shipCount=‘$ships’ WHERE id=‘$tranId’”;
mysql_query( $sql );

See the above code, I have commented out all echo (and exit functions) exept the one for bookContent, I still got garbage displayed other than a download/save dialogue.

See the link: http://books.chnmates.com/delivery.php?tranId=151127125

if they’re all in pdf format an easier option could be to just generate links for the books with the href pointing to the path to the pdf file on the server. the path would be stored in the db table.

when the link is clicked, the file is opened in the browser (if adobe reader is installed) and the user can then save it to wherever they like on their local pc.

you can also put a link on the page pointing directly to the adobe reader download page.

Thanks for the quick reply.
My book content is stored in the MySQL table other than a disc.
My php source code is copied from Kevin Yank’s book: Buld your own database driving website using php & mysql.

How can I do it with the database retrieved content for book?

sorry :(, but I can’t help you with that situation because I would never do it that way and so don’t know the answer.

I always store the physical file (pdf, image, .doc or whatever) on disk and store only the file’s meta data in the database.

As I mentioned, you need to remove the data your echoing before the headers.

The code below is sent before the headers you set, and before the pdf information. If you do as I recommended, your system will work.

So in short: 1) First set the headers 2) Then send the file data

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>&#19979;&#36733;&#20070;&#31821;</title>
  <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
</head>

<body>
  <!--h1&#35874;&#35874;&#36141;&#20080;,&#27426;&#36814;&#19979;&#36733;! h1-->

Thanks, TheRedDevil,

What should I do exactly with this piece of code:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”
http://www.w3.org/TR/html4/strict.dtd”>

<html xmlns=“http://www.w3.org/1999/xhtml”>
<head>
<title>下载书籍</title>
<meta http-equiv=“content-type” content=“text/html; charset=utf-8”/>
</head>

<body>
<!–h1谢谢购买,欢迎下载! h1–>

<?PHP
$tranId = $_GET[ ‘tranId’ ];

echo $bookContent;

// update the download record
$ships = $ships + 1;
$sql = “UPDATE transactions SET shipCount=‘$ships’ WHERE id=‘$tranId’”;
mysql_query( $sql );
?>
<!p谢谢下载。p–>
</body></html>


If you want to provide the information you got in your database as a downloadable file, then you need to remove the code.

I am not sure how else to explain this, you need to REMOVE all text/code/spaces etc I.e. not even any whitespace should be sent from the script.

The only thing you want to send out is, the various headers and then afterwards you should send the file data.

<?PHP
$tranId = $_GET[ ‘tranId’ ];

$bookContent = $book[ ‘content’ ];
$bookSize = strlen( $bookContent );
$disposition = ‘attachment’;

if( strpos( $_SERVER[ ‘HTTP_USER_AGENT’ ], ‘MSIE 5’ ) ||
strpos( $_SERVER[ ‘HTTP_USER_AGENT’ ], ‘Opera 7’ ) )
{
$mimeType = ‘application/x-download’;
}

header( “content-disposition:$disposition; filename=$bookName” );
header( “content-type:$mimeType” );
header( “content-length:$bookSize” );
//header(“Cache-Control: no-cache, must-revalidate”);
//header(‘Content-Transfer-Encoding: binary’);

echo $bookContent;

// update the download record
$ships = $ships + 1;
$sql = “UPDATE transactions SET shipCount=‘$ships’ WHERE id=‘$tranId’”;
mysql_query( $sql );

?>

Hi expert,
I am stuck here. I have cut the PHP file to be like the above without any html code, I still got garbage displayed on the screen, any further help please?
Thanks in advance. link: http://books.chnmates.com/delivery.php?tranId=151127125
Charlie

If you take a look on the source of the page, you will see this at the top:

Top of page:

%PDF-1.3%
1 0 obj [/PDF /Text /ImageB /ImageC /

As you can see it seems to be a linebreak at the top. Are you certain there is no white space at all on the page that is outside the php code?

Also in the code you have this “…” what exactly do you mean by that, if you mean that there is more code there, then that might be where your issue lies.

Keep looking for the whitespace and you will fix the problem.

Good Luck

<?PHP
$tranId = $_GET[ ‘tranId’ ];

$dbconn = @mysql_connect( ‘localhost’, ‘xxx’, ‘xxx’ );
@mysql_select_db( ‘chnmates_com_-_books’ );
$sql = “SELECT id, shipCount, bookId FROM transactions WHERE id=‘$tranId’”;

$tranList = @mysql_query( $sql );
$tran = @mysql_fetch_array( $tranList );

$bookId = $tran[ ‘bookId’ ];
$ships = $tran[ ‘shipCount’ ];
$tranId = $tran[ ‘id’ ];
$sql = “SELECT name, mimetype, content FROM books WHERE id=‘$bookId’”;
$books = @mysql_query( $sql );

$book = @mysql_fetch_array( $books );
$bookName = $book[ ‘name’ ];
$mimeType = $book[ ‘mimetype’ ];
$bookContent = $book[ ‘content’ ];
$bookSize = strlen( $bookContent );
$disposition = ‘attachment’;

header( “content-disposition:$disposition; filename=$bookName” );
header( “content-type:$mimeType” );
header( “content-length:$bookSize” );

echo $bookContent;

?>

Hi TheRedDevil,
Thanks for reply, the above is the full, complete PHP file exept the user name/password removed, I still got the garbage displayed other than dowloand/save, I really don’t know what’s wrong, can you have a look?
http://books.chnmates.com/delivery.php?tranId=151127125

I cant see any direct errors in that code, but you do have whitespace somewhere.
Turn on error reporting and see if that will help you out.

This is the headers you are sending out now, as you can see it does not contain the headers you set by PHP. This indicate you are getting error messages when you try to set the headers.

HTTP/1.1 200 OK
Date: Wed, 01 Dec 2010 18:55:07 GMT
Server: Apache
X-Powered-By: PHP/4.3.9
Vary: Host,Accept-Encoding
Content-Encoding: gzip
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html

Oh. Thanks for the tip. I did find the error log that reads something like this:
“Cannot modify header information”
I googled for a while I saved the PHP file as ansi code other than utf-8 code. The problem solved. I got download/save pop up.
Thanks again.

No problems, Im glad you got it working.

If you dont mind, what editor/IDE do you use? Im just asking as we always use UTF-8 in our company for the files, and we dont have any problems. So it might be located towards the editor you use.

I use simple notepad built in Windows & use Filezilla for upload. I want utf-8 as I want to display some chinese characters after download. Not sure how to solve this.

In regarding Kalon’s alternative, how would you stop people from doing multiple downloads? File safety issue?

Thanks.

You should use a proper editor, then you wont have that problem with the charset.

Give Netbeans a try http://www.netbeans.com/ its a free editor.

For the question regarding stopping people from doing multiple download, that one is harder to do. However I am not sure why you would want to do this? As the same person would not download the same file multiple times?

Thanks for the tip. I just downloaded netbeans for PHP only package & followed its project wizard to use defaul encoding of utf-8, retyped in my delivery.php file, saved. Then uploaded to web host. Download/save window poped up in browser. Great. Not sure why notepad got me wrong when file saved as utf-8 encode.

I also changed the “echo book[‘content’];” which was from database to “echo readfile(‘filename’);” whcih directly reads the disc file. They all worked.

I used database content for download because I think it is safer to protect your book files. Any comment?
I used a download count for each transaction id in database so no multiple downloads is allowed. I don’t want people to distribute the download link. Any comment.

This is a great discussion, thanks a lot.
Charlie

If your saving the files in the database or not is a preference. Both has its advantages and disadvantages.

The problem with download counters is that its not certain the user managed to successfully download the file. It might be a better idea to just allow logged in members to download.