Page 1 of 2

[SOLVED] Uploading file to Longblob is corrupt?

Posted: Mon Jul 17, 2006 6:20 am
by rustynail
Hi all.

I want to be able to upload both binary and ascii files. The code below seems to work and have been able to upload pdfs, jpgs and zips. The size of the file is correct but when I go to view the file, it seems to be corrupted. Say for example I go to retrieve the file with the default application, the applications say no data, corrupt, etc.


Table configuration:

Code: Select all

$query="CREATE TABLE tbl_Files (id_files tinyint(3) unsigned NOT NULL auto_increment,bin_data longblob NOT NULL,description tinytext NOT NULL,filename varchar(50) NOT NULL,filesize varchar(50) NOT NULL,filetype varchar(50) NOT NULL,PRIMARY KEY (id_files))";
Upload script:

Code: Select all

<?php
include "voc_header.php";

if ($_POST['action'] == "upload") {
  // ok, let's get the uploaded data and insert it into the db now
$binFile='';
$filetype = $_FILES['binFile']['type'];
$filename = $_FILES['binFile']['name'];
$filesize = $_FILES['binFile']['size'];




    $binFile=$_POST['binFile'];
    $txtDescription=$_POST['txtDescription'];
    $MAX_FIlE_SIZE=$_POST['MAX_FILE_SIZE'];

    $db = mysql_connect("localhost", "$username", "$password");
    mysql_select_db($database, $db) or die(mysql_errno() . ": " . mysql_error() . "<br>");

  if (isset($filename) && $filename != "") {
    $data = fread(fopen($_FILES['binFile']['tmp_name'], "rb"), $_FILES['binFile']['size']);
    $data = base64_encode($data);
    $strDescription= "(\"${filetype}\",\"${filename}\",\"${data}\")";
    $sql = "INSERT INTO tbl_Files ";
    $sql .= "(description, bin_data, filename, filesize, filetype) ";
    $sql .= "VALUES ('$txtDescription', '$data', ";
    $sql .= "'$filename', '$filesize', '$filetype')";
    $result = mysql_query($sql, $db);
    mysql_free_result($result); 

    echo "<p><center>The new file was successfully added to the database.<br><br>";
    echo "<a href='upload_file.php'><font color='white'><u>Upload Another File</u></font></a></center>";
  } else { echo "<p><center>There was an error in your upload, please go back and correct any errors</center>"; }
  mysql_close();

} else {
    echo "<a href='upload_view.php'><font color='white'><u>View Uploads</u></font></a></center>";
?>
Here is the code I am using to retrieve the file.

Code: Select all

<?php

    $id_files=$_GET['id_files'];
include("dbinfo.inc.php");
    $db = mysql_connect("localhost", "$username", "$password");
    mysql_select_db($database, $db) or die(mysql_errno() . ": " . mysql_error() . "<br>");
if ($id_files) {
  $sql = "SELECT bin_data, filetype, filename, filesize FROM tbl_Files WHERE id_files=$id_files";
	
  $result = @mysql_query($sql, $db);
  $data = @mysql_result($result, 0, "bin_data");
  $name = @mysql_result($result, 0, "filename");
  $size = @mysql_result($result, 0, "filesize");
  $type = @mysql_result($result, 0, "filetype");
	
  header("Content-type: $type");
  header("Content-length: $size");
  header("Content-Disposition: attachment; filename=$name");
  header("Content-Description: PHP Generated Data");
  echo $data;
}
?>
I have looked over this on and off for quite awhile and have tried different methods but the results are the same. Any ideas?

Posted: Mon Jul 17, 2006 7:30 am
by timvw
I don't see you base64_decode...

Posted: Mon Jul 17, 2006 7:52 am
by rustynail
timvw wrote:I don't see you base64_decode...
Thanks for the fast reply. With your suggestion, I did this with the same results:

Code: Select all

<?php

    $id_files=$_GET['id_files'];
include("dbinfo.inc.php");
    $db = mysql_connect("localhost", "$username", "$password");
    mysql_select_db($database, $db) or die(mysql_errno() . ": " . mysql_error() . "<br>");
if ($id_files) {
  $sql = "SELECT bin_data, filetype, filename, filesize FROM tbl_Files WHERE id_files=$id_files";
	
  $result = @mysql_query($sql, $db);
  $data = @mysql_result($result, 0, "bin_data");
  $datafix = base64_decode($data);
  $name = @mysql_result($result, 0, "filename");
  $size = @mysql_result($result, 0, "filesize");
  $type = @mysql_result($result, 0, "filetype");
	
  header("Content-type: $type");
  header("Content-length: $size");
  header("Content-Disposition: attachment; filename=$name");
  header("Content-Description: PHP Generated Data");
  echo $datafix;
}
?>
The file uploaded and the file downloaded are the same size.

Posted: Mon Jul 17, 2006 7:57 am
by rustynail
I do notice that if I upload an ascii file that the download includes a single space right at the start of the file contents.

Posted: Mon Jul 17, 2006 9:11 am
by rustynail
Here is a test that I had done. I uploaded the file code.zip and then downloaded it to code1.zip. Opened them up in a hex editor to check out the code. Here is what I found (circled in red is the differences):

Image

If I edit the beginning and end code1.zip to that of code.zip, then the file opens up correctly. I don't see anything in my script that would do this.

Posted: Tue Jul 18, 2006 7:59 am
by rustynail
I have tried my scripts on 3 different servers. Same operating system, php version, and mysql version - with the same results. MySQL is version MySQL - 4.1.12-NDB if that makes any difference.

Posted: Sun Jul 30, 2006 4:32 pm
by rustynail
Not into bumping, but I have been still searching to why this is happening. Any ideas?

Posted: Sun Jul 30, 2006 5:16 pm
by volka
What does

Code: Select all

echo "<p><center>The new file was successfully added to the database.<br><br>";
    echo '<fieldset><legend>Debug</legend>',
    		'file size: ', $_FILES['binFile']['size'], "<br />\n",
    		'base64 length: ', strlen($data), "<br />\n",
    		'base64 4/3 length: ', strlen($data)*3/4, "<br />\n",
    		"<fieldset>\n";
    echo "<a href='upload_file.php'><font color='white'><u>Upload Another File</u></font></a></center>";
echo?

Posted: Sun Jul 30, 2006 5:40 pm
by rustynail
volka wrote:What does

Code: Select all

echo "<p><center>The new file was successfully added to the database.<br><br>";
    echo '<fieldset><legend>Debug</legend>',
    		'file size: ', $_FILES['binFile']['size'], "<br />\n",
    		'base64 length: ', strlen($data), "<br />\n",
    		'base64 4/3 length: ', strlen($data)*3/4, "<br />\n",
    		"<fieldset>\n";
    echo "<a href='upload_file.php'><font color='white'><u>Upload Another File</u></font></a></center>";
echo?
Thanks!! Looks like some ideas coming :D

Code: Select all

file size: 124937
base64 length: 166584
base64 4/3 length: 124938

Posted: Sun Jul 30, 2006 6:24 pm
by volka
rustynail wrote:Thanks!! Looks like some ideas coming :D
Sorry, still wild guessing.
Hm, at least the values are coherent.

Ok, then let's take a look at the retrieval...

Code: Select all

<?php
$id_files=$_GET['id_files'];
require 'dbinfo.inc.php';

$db = mysql_connect('localhost', $username, $password);
mysql_select_db($database, $db) or die(mysql_errno() . ": " . mysql_error() . "<br>");
if ($id_files) {
  $sql = "SELECT bin_data, filetype, filename, filesize FROM tbl_Files WHERE id_files=$id_files";
  $result = @mysql_query($sql, $db) or die(mysql_error());
  $row = mysql_fetch_array($result);
  if(!$row) {
  	die($id_files. ' not present');
  }
  
  $datafix = base64_decode($row['bin_data']);
  
  echo 'row[filesize]: ', $row['filesize'], "<br />\n";
  echo 'strlen(bin_data): ', strlen($row['bin_data']), "<br />\n";
  echo 'strlen(datafix): ', strlen($datafix), "<br />\n";
  echo 'ord: ';
  for($i=0; $i<10; $i++) {
  	printf('%02x ', ord($datafix{$i}));
  }
}
?>
(please for the same file as before ...of course ;))

btw:
WHERE id_files=$id_files
you should search the board for sql injection

Posted: Sun Jul 30, 2006 9:38 pm
by rustynail
Thanks again for the help. I forgot which file it was that I originally tested 8O So I re-ran the first with another file. Here are the results.

file size: 191238
base64 length: 254984
base64 4/3 length: 191238

row[filesize]: 191238
strlen(bin_data): 254984
strlen(datafix): 191238
ord: ff d8 ff e0 00 10 4a 46 49 46

Posted: Mon Jul 31, 2006 7:39 am
by volka
Oh, could you please re-run the test with a zip archive.
We know it should start with PK, and the printf/ord thing should print it as 50 4b.

And while you're at it ;)
please add

Code: Select all

$data = fread(fopen($_FILES['binFile']['tmp_name'], "rb"), $_FILES['binFile']['size']);
		
		echo '<fieldset><legend>Debug: data/ord</legend>';
		for($i=0; $i<10; $i++) {
			printf('%02x ', ord($datafix{$i}));
		}
		echo "<fieldset>\n";
		
		$data = base64_encode($data);
to the upload script and

Code: Select all

'base64 4/3 length: ', strlen($data)*3/4, "<br />\n",
				'base64 data: ', substr($data, 0, 24),
			"<fieldset>\n";
so that we can find out where this extra-character is prepended.

Posted: Mon Jul 31, 2006 10:26 am
by rustynail
Thanks again. Sure hope I am following what needs inserted and where. But here you go.

with test.zip

upload:
file size: 674
base64 length: 900
base64 4/3 length: 675
50 4b 03 04 14 00 00 00 08 00

download:
row[filesize]: 674
strlen(bin_data): 900
strlen(datafix): 674
ord: 50 4b 03 04 14 00 00 00 08 00

One on left original and the one on the right the downloaded.
Image

Posted: Mon Jul 31, 2006 10:52 am
by volka
hm hm, very strange....
strlen(datafix): 674
ord: 50 4b 03 04 14 00 00 00 08 00
This indicates, that $datafix contains proper data.
So it's not the database and/or base64 failing but the output to the client...
What's the hexcode of this extra-character according to KHexEdit? (the altered file is in background so I cannot see ;))

Where's that character if you change the script this way:

Code: Select all

header("Content-type: $type");
  header("Content-length: $size");
  header("Content-Disposition: attachment; filename=$name");
  header("Content-Description: PHP Generated Data");
  echo '-->', $data;
before or after the --> ?
Is it possible that this character is printed by your script? Maybe a blank before the first <?php or something like that.

Posted: Mon Jul 31, 2006 11:57 am
by rustynail
I looked into my scripts to see if there was a space, but nothing stands out. Is it possible that the version of MySQL that I am using has a bug or maybe even Apache or operating system?

Here's a screen grab of the hex info.

Image

This is the exact code that I used to retrieve the file:

Code: Select all

<?php
    $file_name='';
    $file_name=$_GET['filename'];

include("dbinfo.inc.php");
    $db = mysql_connect("localhost", "$username", "$password");
    mysql_select_db($database, $db) or die(mysql_errno() . ": " . mysql_error() . "<br>");
if ($file_name) {
  $sql = "SELECT bin_data, filetype, filename, filesize FROM tbl_Files WHERE filename='$file_name'";
	
  $result = @mysql_query($sql, $db);
  $data = @mysql_result($result, 0, "bin_data");
  $datafix = base64_decode($data);
  $name = @mysql_result($result, 0, "filename");
  $size = @mysql_result($result, 0, "filesize");
  $type = @mysql_result($result, 0, "filetype");
	
  header("Content-type: $type");
  header("Content-length: $size");
  header("Content-Disposition: attachment; filename=$name");
  header("Content-Description: PHP Generated Data");

  echo $datafix;

}
?>
I just downloaded to another client (windows) and viewed the file - same results HEX 20 as the first character.