Problem using PHP to pull binary files from a BLOB field in MYSQL

Well, I’m not sure if this belongs more in the MySQL forum or here, but it seems like I’m posting mostly PHP code so here goes.

I’ve searched high and low, and the best I’ve been able to find is a post on this user forum, so I registered to ask for your expertise. (Here is that post.)

I am trying to upload binary files to a table in MySQL. (I know that this is some resistance to doing this, but this is the route we’ve decided to go down for the project I’m working on.)

Here is the code for my upload form:

<html>
<form method="post" enctype="multipart/form-data" action="grabfile.php">

<input type="hidden" name="MAX_FILE_SIZE" value="2000000">
<input name="userfile" type="file" id="userfile">

<input name="upload" type="submit" class="box" id="upload" value=" Upload ">
</html>

And here is the phpfile that runs when the Upload button is clicked:

<?php
if(isset($_POST['upload']) && $_FILES['userfile']['size'] > 0)
{
$fileName = $_FILES['userfile']['name'];
$tmpName  = $_FILES['userfile']['tmp_name'];
$fileSize = $_FILES['userfile']['size'];
$fileType = $_FILES['userfile']['type'];

$fp      = fopen($tmpName, 'r');
$content = fread($fp, filesize($tmpName));
$content = addslashes($content);
fclose($fp);

if(!get_magic_quotes_gpc())
{
    $fileName = addslashes($fileName);
}

include 'config.php';


$query = "INSERT INTO upload (name, size, type, content ) ".
"VALUES ('$fileName', '$fileSize', '$fileType', '$content')";

mysql_query($query) or die('Error, query failed');


echo "<br>File $fileName uploaded<br>";
}

?>

This nicely puts the selected file into my DB. However, when I try to download the file, there seems not be any character encoding information. The entire file downloads–or at least, a file of the same size downloads–but nothing can open the file. JPGs and JPEGs can’t be opened (on Windows or Linux), and a .doc prompts me to select the character set. But no matter which character set I choose, the file is just gibberish.

Here is the code I’m trying to download the file with:

<?php

    include 'config.php';

    if(isset($_GET['id']))
{
    $id=intval($_GET['id']);
    $query = "SELECT name, type, size, content FROM upload WHERE id=$id";
    $result = mysql_query($query) or die('Error, query failed');
    list($name, $type, $size, $content) = mysql_fetch_array($result);
    header("Content-Disposition: attachment; filename=$name");
    header("Content-length: $size");
    header("Content-type: $type");
    echo $content;

    exit;

}

?>

<html>
	<head>
		<title>Download File From MySQL</title>
		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	</head>
<body>

<?php
    $query  = "SELECT id, name FROM upload";
   $result = mysql_query($query) or die('Error, query failed');
    if(mysql_num_rows($result) == 0)
    {
    echo "Database is empty <br>";
    }else{
    while(list($id, $name) = mysql_fetch_array($result))
{
?>
   <a href="download.php?id=<?php echo $id ?>"><?php echo $name ?></a> <br/>

<?php
    }
    }
?>

</body>

</html>

In case this helps, the columns for upload in MySQL look like this:

mysql> show full columns in upload;
+---------+-------------+-------------------+------+-----+---------+----------------+----------------------+---------+
| Field   | Type        | Collation         | Null | Key | Default | Extra          | Privileges           | Comment |
+---------+-------------+-------------------+------+-----+---------+----------------+----------------------+---------+
| id      | int(11)     | NULL              | NO   | PRI | NULL    | auto_increment | select,insert,update |         |
| name    | varchar(30) | latin1_swedish_ci | NO   |     | NULL    |                | select,insert,update |         |
| type    | varchar(30) | latin1_swedish_ci | NO   |     | NULL    |                | select,insert,update |         |
| size    | int(11)     | NULL              | NO   |     | NULL    |                | select,insert,update |         |
| content | mediumblob  | NULL              | NO   |     | NULL    |                | select,insert,update |         |
+---------+-------------+-------------------+------+-----+---------+----------------+----------------------+---------+

Thanks in advance for any insight you can give me.

-Josh

I just tried your files on my localhost WAMP (Win7 Pro x64) server and it works fine for me.
The only thing I did have to change was this line:


header("Content-Disposition: attachment; filename=$name");

Into:


header("Content-Disposition: attachment; filename=\\"$name\\"");

(so replace $name with \“$name\”)

I have an images called “clouds 3.jpg” and when I uploaded it and downloaded it again it came back as the file “clouds” (extension-less).

Tested with a .jpg and a .docx, both work as expected. The database table I created is an exact replica of yours.

It could be a problem in the config.php? This is the one I used:


$link = mysql_connect('localhost', '***', '***') or die ('Could not connect to db!');
mysql_select_db('***', $link) or die ('Unable to select db!');
mysql_set_charset('utf-8');

Also tried the iso-8859-1 charset, and that works as well.

As a last idea, maybe you’ve got some unwanted magic quotes going on somehere?

ScallioXTX,

Thanks so much for your help. Sadly, I’m still stuck.

I tried escaping $name and adding mysql_set_charset(‘utf-8’); to my config.php file. But I still cannot open files that I’ve downloaded from the db. Also checked that magic quotes are turned off. We’re running PHP 5.2.14.

When I try to open a jpeg in linux, I get this message:
Error interpreting JPEG image file (Not a JPEG file: starts with 0x0a 0xff)
Does that bit of hex code give any clues?

If my script works for you, then it must be something in the way my MSQL installation or database is configured, right? Or maybe the way our isp has php configured?

Thanks,

-Josh

Are both magic_quotes_gpc and magic_quotes_runtime set to off?

Otherwise I don’t know what the problem may be. I’m running PHP 5.2.8 with error_reporting = E_ALL | E_STRICT by the way.

Yep, magic_quotes_gpc and magic_quotes_runtime are both disabled.

I’ve even tried recreating the table as InnoDB and switching it back to MyISAM, but still no joy. I’ll see if there’s some way to upgrade PHP on the ISPs side.

Thanks for the input. At least I can kind of rule out code problems now.

-Josh

Just thought of something.

If you change grabfile.php temporarily such that it directly sends the just uploaded file back to the browser directly and that works you can rule out a problem with MySQL. If that doesn’t work the problem is with MySQL.

grabfile.php


<?php
if(isset($_POST['upload']) && $_FILES['userfile']['size'] > 0)
{
$fileName = $_FILES['userfile']['name'];
$tmpName  = $_FILES['userfile']['tmp_name'];
$fileSize = $_FILES['userfile']['size'];
$fileType = $_FILES['userfile']['type'];

$fp      = fopen($tmpName, 'r');
$content = fread($fp, filesize($tmpName));
fclose($fp);

header("Content-Disposition: attachment; filename=\\"$fileName\\"");
header("Content-length: $fileSize");
header("Content-type: $fileType");
echo $content;
exit();

:slight_smile:

If your file is indeed binary, this will surely bugger it up.


$content = addslashes($content);

AnthonySterling,
I commented out the addslashes line, but then the query failed.

ScallioXTX,

I tried your suggestion and used your posted code to bypass the DB. Indeed, the file opens beautifully. So this means that something bad is happening on the way to the DB, in the DB, or on the way from the DB, correct?

I’ve got a post in on my ISPs forum. It does not seem heavily-read, but I’m hopeful that someone there will have an insight that can help me.

In the mean time, if anyone thinks of anything, I’ll be happy to try it. I really really appreciate the time you guys are taking for this. It’s been a real headache for me!

I also tried that and the query failed for me as well.

Absolutely 100% correct :slight_smile:

You’re very welcome. I’m here to help :slight_smile:

OK, this is really getting weird now. The folks at my ISP were not very helpful, since no one is ever very keen on putting binary data in a DB. So I figured I’d install a LAMP configuration on my localhost just to make sure that the problem is on the server side.

But even on localhost, I have the exact same problem! However, it turns out that PDFs download just fine–both from the server and from localhost.

So this is a real head scratcher. I guess I will go back to the drawing board and see if I can find another script that works.

If anyone has any ideas, I’ll be thrilled to hear them.

Thanks!

-Josh

Curiouser and curiouser!

I just looked at your code and suddenly noticed $fp = fopen($tmpName, 'r'); which opens the file for reading, but does not specify the file is binary.

Could you try with replacing


$fp      = fopen($tmpName, 'r');
$content = fread($fp, filesize($tmpName));
$content = addslashes($content);
fclose($fp);

with


$content = add_slashes( file_get_contents($tmpName) );

since file_get_contents is binary-safe :slight_smile:

If this doesn’t work I’m totally out of ideas, so let’s hope it does eh? :wink:

Well, file_get_contents did no help me :frowning:
PHP says that this function is binary safe, but there may be a bug.

I also tried readfile instead of fopen. Same result.

So then I wondered if this was a bug in the version of php I’m using. So I booted into Windows 7, Installed WAMPSERVER, Added php 5.2.8, Recreated my scripts etc. And guess what? It still didn’t work! ScallioXTX this should be the same configuration that you’ve used with success, right? So there must be something different between what I’m doing and what you’re doing. Could I trouble you to post or send the output of your phpinfo() ?

Also, I’m trying to figure out if the files are being garbled on their way to the DB or from the DB. Does anyone know of a way to just pluck the BLOB file out of MYSQL without using the php scripts I’ve written? I’ve tried with PhpMyAdmin, but if it’s possible to do this, I’m not seeing it.

Thanks!

-Josh

I’ve just sent you a PM with my complete config. Hope that helps :slight_smile:

I use SQLYog for viewing and accessing the database and there I can click a blob field and see the image. There is a free version of this program available as well, but I don’t know if that supports it (I have the commercial version).

welsh059, have you tried:

$uploaded_file['data']         = file_get_contents($_FILES['upload']['tmp_name']);

SpacePhoenix,
I gave that a try and it did not fix the problem. Thanks though!

ScallioXTX,
Thanks much for the configuration files. Sadly, they didn’t fix the problem either.
But…

After installing SGLyog (the trial version–<strike>the open source version doesn’t allow the viewing of Blob data, that I could see at any rate.</strike>–my bad–the community version does allow viewing of BLOB data!) I connected to the DB and double clicked on a BLOB field and lo and behold! My images have been in the DB, happy as clams all along!

This seems like great news because now I have narrowed the problem down to my download scripts, right? MySQL is configured fine. PHP is allowing me to upload. I guess it could be PHP or Apache messing me up on the download side of things.

Actually, I never did get your httpd.conf to work in my WAMP stack. Can anyone think of a field there that would be corrupting files on the way downstream?

Here are the two scripts I’m using to download. I apologize for dumping the html in too, but I want to include it all, in case these is some small oversight that is making these not work.

This is the file that generates the list of files in the DB:

<?php

    include 'config.php';

    if(isset($_GET['id']))
{
    $id=intval($_GET['id']);
    $query = "SELECT name, type, size, content FROM upload WHERE id=$id";
    $result = mysql_query($query) or die('Error, query failed');
    list($name, $type, $size, $content) = mysql_fetch_array($result);

    header("Content-Disposition: attachment; filename=\\"$name\\"");
    header("Content-length: $size");
    header("Content-type: $type");
    echo $content;

    exit;

}

?>

<html>
	<head>
		<title>Download File From MySQL</title>
		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	</head>
<body>

<?php
    $query  = "SELECT id, name FROM upload";
   $result = mysql_query($query) or die('Error, query failed');
    if(mysql_num_rows($result) == 0)
    {
    echo "Database is empty <br>";
    }else{
    while(list($id, $name) = mysql_fetch_array($result))
{
?>
   <a href="download.php?id=<?php echo $id ?>"><?php echo $name ?></a> <br/>

<?php
    }
    }
?>
<a href ="getfiles.php">Upload another file</a>
<br />

<br />

</body>

</html>

And here is the script that processes the download:

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
if(isset($_GET['id']))
{
// if id is set then get the file with the id from database

include 'config.php';

$id    = $_GET['id'];
$query = "SELECT name, type, size, content FROM upload WHERE id = '$id'";

$result = mysql_query($query) or die('Error, query failed');

  $name = @mysql_result($result, 0, "name");
  $size = @mysql_result($result, 0, "size");
  $type = @mysql_result($result, 0, "type");
  $content = @mysql_result($result, 0, "content");


header("Content-length: $size");
header("Content-Type: $type");
header("Content-Disposition: attachment; filename=\\"$name\\"");
header("Content-Description: PHP Generated Data");
header("Content-transfer-encoding: binary");

echo $content;


exit;
}

?>

Any ideas?

Thanks again for the help. I think I’m getting close here!

-Josh

Eureka!

Stumbled across this little line of code:

while (@ob_end_clean());

The forum I found it says that “if you server has output buffering on, then it won’t send the image data correctly.”

So now, at long last, I can download the images, pdfs, etc. from the DB!

Thank you all SO much for your help with this. My boss is ecstatic, and I will be sleeping much more soundly tonight. SitePoint rocks!

-Josh

1 Like