I am attempting to store files in a MySql database based upon this article. Before you suggest I store files on the file system and not in the database the files uploaded will be stored and relate to records in the database. I could keep a path to the files and just the file names but I want users to be able to modify and delete the files directly through the web interface. So, let's just pretend that storing in the database is a good option and not discuss others.
My issue is when I go to download the file the data returned from the BLOB in the MySql database is not exactly what I uploaded in test. If I use the function of addslashes then binary wise it is completely off. If I remove that function from the routine then I get much closer.
Using a Binary Diff application I can see that, while not using addslashes the file data returned has one additional byte of data at the beginning of the file and in turn one less byte at the end. The remainder of the file's data is offset by one due to this little feature of the download.
I am using a FORM to submit the data and enctype is set to multipart/form-data as it should be. Here's some code I'm using to get it all to work. DAL in the code references a class I created for my Data Access Layer. It handles all saving and retrieving routines and doesn't seem to be injecting any new data. I've checked by looking at the file's content before it goes to my DAL class for saving and the extra byte is already there.
upload
if(isset($_POST['upload']) && $_FILES['userfile']['size'] > 0)
{
$fp = fopen($_FILES['userfile']['tmp_name'], 'r');
$_FILES['userfile']['content'] = fread($fp, $_FILES['userfile']['size']);
$_FILES['userfile']['content'] = addslashes($_FILES['userfile']['content']);
fclose($fp);
if(!get_magic_quotes_gpc())
{
$_FILES['userfile']['name'] = addslashes($_FILES['userfile']['name']);
}
$fileDAL = new DAL("files");
$newID = $fileDAL->SaveItem($_FILES['userfile'], false);
echo "<br>File ".$_FILES['userfile']['name']." uploaded as ID $newID<br>";
}
download
if(isset($_GET['id']))
{
$id = $_GET['id'];
$objFiles = new DAL('files');
$objFile = $objFiles->GetItem($id);
header("Content-length: $objFile->size");
header("Content-type: $objFile->type");
header("Content-Disposition: attachment; filename=$objFile->name");
echo $objFile->content;
exit;
}
use mysql_real_escape_string().
excerpt from its man page:
Escapes special characters in the unescaped_string, taking into account the current character set of the connection so that it is safe to place it in a mysql_query(). If binary data is to be inserted, this function must be used.
so I would change the escaping part of your code to:
if( get_magic_quotes_gpc() )
{
$_FILES['userfile']['name'] = stripslashes( $_FILES['userfile']['name'] );
}
$_FILES['userfile']['name'] = mysql_real_escape_string( $_FILES['userfile']['name'] );
Turns out that I add an extra line feed or carriage return at the end of a PHP file that was in my headers prior to this code being executed. It caused the header upon writing to include the line feed (OA) in the binary data for file saving.
Related
While I'm sending file ( type="file") to database (PHPmyAdmin), instead of saving with file name it saves as BLOB as shown in the following figure.
I found the the similar question Saving Files as blob in database ajax php pdo
here but didn't help me. My php code is as follows for sending file to the database.
$info = pathinfo($_FILES['file']['name']);
$ext = $info['extension']; // get the extension of the file
$newname = $get.".".$ext; //$newname='newfilename.'.$ext
$target ="folder/".$newname;
$doc= move_uploaded_file( $_FILES['file']['tmp_name'], $target);
$sql="INSERT INTO apply(mobile,doc,position)
VALUES (' $mobile','$newname',' $position')";
$query=mysqli_query($conn, $sql);
The file is sending to target folder (named folder) with the correct required name but Why I'm not getting the same name in the database. What's going wrong in my code?
Thanks in advance.
First of all, you should not tore files into the database directly because of critical performance issues.
Suggested way is to store files in file system, and store their path in database as TEXT or Varchar.
Now coming to your question:
While I'm sending file ( type="file") to database (PHPmyAdmin), instead of saving with file name it saves as BLOB as shown in the following figure.
Every file is composed of binaries but in different order and format. Storing it in database is not actually tricky. Database converts them into array of bytes/ stream of bytes and then stores these byte format data into the table.
When a fetch query is fired, it returns the exact byte stream in response. And at application level, these bytes are treated as files by their respective encoding techniques.
It's worth reading this post and subsequent link in the accepted answer.
Save image to database
$query =$myClass->query("insert into tblImage(document) values(".base64_encode(file_get_contents($_FILES['imageFile']["tmp_name"])).")");
Show image from database
$image =$myClass->query("select document from tblImage where code=1");
$output_bin_string = base64_decode($image[0]['thumbDocument']);
header("Content-Type: image/jpeg");
header("Content-Length: " . strlen($output_bin_string));
echo $output_bin_string;
I can save image to file and then save to database
$image=$myClass->query('select document form tblImage where code=1' );
$source = imagecreatefromstring(base64_decode($image[0]["document"])) ;
$rotate = imagerotate($source,$degrees,0);
imagejpeg($rotate,'tmp/1.jpg');
$image =$myClass->query("update tblImage set document='".base64_encode(file_get_contents('tmp/1.jpg'))."' where code=1");
Question: there is way that rotate image and save to database without save image to file like this
$image=$myClass->query('select document form tblImage where code=1' );
$source = imagecreatefromstring(base64_decode($image[0]["document"])) ;
$rotate = imagerotate($source,$degrees,0);
$image =$myClass->query("update tblImage set document='".base64_encode($rotate)."' where code=1");
This code say error:base64_encode() expects parameter 1 to be string, resource given
Saving files in a database is a bad practice and performs pretty bad when comparing to native file system access or another storage backend like S3. Have a look at this question: Saving images in database mysql
Your approach will cause the data to go through the DB, php and the webserver before it goes to the client. While saving it directly in the file system and keeping a reference in the DB to the file will allow you to send it directly through the webserver when it is requested.
A proper approach is to have one table, lets call it file_storage for example, in the application that keeps the references to files. Then associate the rows as needed: User 1:1 Avatar, User 1:n Photo, Photo n:n Gallery for example. This is DRY, maintains SoC and even allows me to store files in different backends. You can read as well the documentation of this plugin for CakePHP that does exactly what I just described.
For image manipulation I recommend using the Imagine library. Here is an example taken from the documentation:
use Imagine\Image\Box;
use Imagine\Image\Point;
$image->resize(new Box(15, 25))
->rotate(45)
->crop(new Point(0, 0), new Box(45, 45))
->save('/path/to/new/image.jpg');
You should first convert your image resource to a string (by buffering the output into a variable), and then save it to your database like this:
// ... your previous code
$rotate = imagerotate($source,$degrees,0);
// New code starts here
ob_start();
imagejpeg($rotate);
$imageData = ob_get_clean();
$image =$myClass->query("update tblImage set document='".base64_encode($imageData)."' where code=1");
Note that this must be done before sending any output to the client, otherwise ob_start() will fail.
I'm making a web-based system using html and php, one of the functions is to allow students to upload files to the database, 3 files actually.
I made an input field of type file and I'm sure my PHP is correct but sometimes a warning I don't understand appears and the query doesn't work. but this does not always happen.. usually not..
Here's one of the fields in the HTML:
<tr><td> <p><span>C.V: </span> </td><td> <input class="contact" type="file" id ="CV" name="CV" value="" required /></p> </td></tr>
and here's the PHP
//check for CV
if ($_FILES["CV"]["size"] > 0)
{
$fileName = $_FILES['CV']['name'];
$tmpName = $_FILES['CV']['tmp_name'];
$fileSize = $_FILES['CV']['size'];
$fileType = $_FILES['CV']['type'];
//check if larger than 1M
if($fileSize > 1048576) {echo "<p style='color: red;'> Cannot upload <b>CV</b> due to large size. File must be less than 1MB </br> </p>"; $complete= false;}
//check if .PDF ( I need it to be PDF always)
else if($fileType != "application/pdf")
{
echo "<p style='color: red;'> Cannot upload <b>CV</b> file type must be a .PDF only </br> </p>"; $complete= false;}
//everything is fine:
else{
$fp = fopen($tmpName, 'r');
$CV = fread($fp, filesize($tmpName));
$CV = addslashes($CV);
fclose($fp);
if(!get_magic_quotes_gpc())
{
$fileName = addslashes($fileName);
}
echo "<br>CV: File $fileName uploaded with type $fileType and size $fileSize <br>";
}
}
SQL Query:
$qry=" UPDATE student SET CV = '$CV', Transcript='$tran',IELTScertificate='$EC', Status ='$stat' WHERE KSUID ='$KSUID'";
Warnings:
Warning:mysql_query(): MySQL server has gone away...line 330
Warning:mysql_query(): Error reading result set's header...line 330
330. $result=mysql_query($qry);
In the database I didn't use a table for uploaded files, I only need content but I don't care about the type because it'll always be a pdf, and I don't care about the name as long as it's stored in the right column.
I'm not sure if this is the problem? but I don't think so cause some files were successfully uploaded where other files with different size didn't.
Another issue I'm facing with retrieving the files..
when I download the file I get it correctly as a .pdf file but when I open it it says the file got damaged.. I'm not sure why? maybe because I'm using local server? though I don't think so but I'm tired trying to figure out :( if anybody faced such a problem before or if you know how to fix this problem please help me
HTML :
<a href='updatestudentlist.php?id=".$ksuid."&file=00'>CV</a> <br/>
PHP:
if(isset($_GET['file'])){
$file=intval($_GET['file']);
if($file==00) //I use this cause other files got different numbers
{
$query = "SELECT CV FROM student WHERE KSUID='".$_GET['id']."'";
$result = mysql_query($query) or die('Error, query failed');
list($content) = mysql_fetch_array($result);
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=".$_GET['id']."_CV");
echo $content;
exit;
}
}
There are multiple issues at play here, mostly centered around how you're working with the database.
1) You shouldn't be using mysql_query() to interface with your database, because that module has been depreciated in favor of better, more current libraries (such as MySQLi). In particular, one major feature the MySQL extension lacks that MySQLi has is prepared statements, which is indirectly why you're getting the warning about the server going away.
Aside from opening yourself to injection attacks with your current code, it also produces an extremely large SQL query (because you're including the PDF itself inside the query string to update the student profile). If an individual query is taking too long to execute, the connection to the server will be dropped (which issues that particular warning). Thus, you should be getting the error from uploading larger PDFs, whereas smaller PDFs upload just fine.
If instead you use prepared statements, as available in the MySQLi extension, the server will be able to handle the extremely short query and then wait on the parameters to be passed as well, of which the extremely large PDF file-string will be one.
2) Your file is considered "damaged" because you're using addslashes() on it before inserting it into the database, but not using stripslashes() when you pull it out. Thus, the PDF you're serving up has most of the data right, but it has a bunch of extra slashes in it, which throws off the PDF reader.
The first part of question is mostly related to connectivity issues with the MySQL Server. There are a host of reasons that could happen. Check this link Server has gone away for the different reasons
I am using the following scripts to test inserting and then reading blob data.
insertion script:
include('session.php');
$provider =$_POST['provider_id'];
$trd_period =$_POST['trading_period_month'];
$pdf_statement =stream_get_contents(fopen($_FILES['pdf_statement']['tmp_name'], 'rb'));
$pdf_statement_clean=addslashes($pdf_statement);
$insert="update rd_provider_statement
set pdf_statement='".$pdf_statement_clean."', creation_user_id='SCO'
where provider_id='".$provider."' and trading_period_month='".$trd_period."'";
mysql_query($insert);
mysql_query("COMMIT");
echo mysql_error();
Download Script:
include('session.php');
//Gather Post Variables
$TP_Month =$_POST["trading_period_month"];
$provider =$_POST["provider_id"];
$TP_format =substr($TP_Month, 0, 7);
//Download Statement
$sql_qry="select *
from rd_provider_statement
where provider='".$provider."' and trading_period_month='".$TP_Month."'";
$sql_err_no=sql_select($sql_qry,$sql_res,$sql_row_count,$sql_err,$sql_uerr);
$row = mysql_fetch_assoc($sql_res);
$bytes =stripslashes($row['pdf_statement']);
header("Content-type: application/pdf");
header('Content-disposition: attachment; filename="'.$provider.'statement'.$TP_format.'"');
print $bytes;
However, when the file is downloaded it cannot open on the grounds that it is not a supported format. I use the basis of the script on another page to download blob data from the database however the insertion into the database here is done by a mysql procedure and not PHP. I think it is my insertion script that is causing the problem.
try using mysql_real_escape_string() instead of addslashes(). it might fix you problem.
For debugging, you might calculate the md5() of the string before inserting into DB and then after retrieving it. I bet you're going to get different hashes, meaning you're not inserting it correctly and your binary data gets corrupted when inserted into the DB.
Side notes:
don't use inserts like that, use binding - How to bind SQL variables in Php?
check for errors and STOP, dont simply echo them(i hope you're doing this in your production code)
Generally you wouldn't want to have any output code before your http header description. See http://php.net/manual/en/function.header.php
Either store the filename and other file information in a session then just access them in another page.
A few things that you need to check:
max_allowed_packet in my.ini should be equal or higher than the file size that you're expecting to store in the database
check to see if the data type that you selected fits the file that you will store. There's tiny blob, blog, medium blob and long blob. You might want to try the largest which is long blob.
I'm not sure about this one but did you already check if file_get_contents works:
mysql_real_escape_string(file_get_contents($file))
Here's my alternative answer.
First the update query:
Prepare the file (assuming that your file is not a binary file):
$tmpName = $_FILES["pdf_statement"]["tmp_name"];
$fp = fopen($tmpName, 'r');
$data = fread($fp, filesize($tmpName));
$data = addslashes($data);
fclose($fp);
$insert="update rd_provider_statement
set pdf_statement='".$data."', creation_user_id='SCO'
where provider_id='".$provider."' and trading_period_month='".$trd_period."'";
DOWNLOAD:
enter code here
$sql_qry="select provider_id, pdf_statement
from rd_provider_statement
where provider='".$provider."'
and trading_period_month='".$TP_Month."'";
$sql_err_no=sql_select($sql_qry,$sql_res,$sql_row_count,$sql_err,$sql_uerr);
$row = mysql_fetch_assoc($sql_res);
$name=$row['provider_id'];
$file=$row['pdf_statement'];
header("Content-Disposition: attachment; filename=\".$name_statement.$TP_format.\";" );
echo $file;
Hope it helps =)
I have a varbinary(MAX) field in a SQL Server 2005 database. I'm trying to figure out how to insert binary data (ie. an image) into that field using PHP. I'm using ODBC for the connection to the SQL Server database. I have seen a number of examples that explain this for use with a MySql database but I have not been able to get it to work with SQL Server. Thanks.
function prepareImageDBString($filepath)
{
$out = 'null';
$handle = #fopen($filepath, 'rb');
if ($handle)
{
$content = #fread($handle, filesize($filepath));
$content = bin2hex($content);
#fclose($handle);
$out = "0x".$content;
}
return $out;
}
usage:
$out = prepareImageDBString('/img/myimg.png');
mssql_query("INSERT INTO MyTable(MyImage) VALUES($out) ");
MyImage is SQL Server field where the type is image
I won't say it's a bad practice, it depends on how big is the image and how your application use it.
If file below 256K in size, store in db is more efficient;
More than 1 mb, store in file-system is recommended.
Storing images in SQL Server?
The simple answer is: stop what you're doing.
You don't want to store binary files inside a database unless you have some very specific security issues. Instead you want to store their filenames (possibly rename the files to prevent confliction) and then store that in the database. If security is an issue, then put them in a non web folder and use your code to retrieve the file and only serve the file if the user has access to it.
Storing images or files inside a database is a waste of file space, a mis-use of databases, and not letting things do what they do best; the filesystem knows files, the database knows data.