Insert binary data into SQL Server using PHP - php

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.

Related

How to make file_get_contents return an unique result

I am new to php and would like to ask you for some help returning an unique result from file_get_contents(). The reason is I want to give each photo an unique name, so that later it will be possible to delete just one of them and not all.
$file =addslashes(file_get_contents($_FILES['image']['tmp_name'][$key]));
Unfortunately time() and microtime() doesn't help in this situation.
Maybe this will help you: http://php.net/manual/en/function.uniqid.php
uniqid();
$imageName = $imageName . '_' . uniqid();
Why not just generate the SHA-1 of the content and use that as the file name (sort of like how git stores objects in a repository's loose object database)? I typically don't like to insert blobs into the RDBMS; it's a little clunky and you have to make sure the blob field has enough space for the kind of file sizes you're expecting to work with. Plus, the filesystem is optimized to handle, well, files. So it makes sense to keep a special directory on the server to which the Web server has write access. Then you write the files there and store references to them in the database. Here's an example:
// Read the contents of the file and create a SHA-1 hash signature.
$tmpPath = $_FILES['image']['tmp_name'];
$blob = file_get_contents($tmpPath);
$name = sha1($blob) . '.img'; // e.g. b99c6e26c3775fca9918ad614b7be7fe4fd7bee3.img
// Save the file to your server somewhere.
$dstPath = "/path/to/imgdb/$name";
move_uploaded_file($tmpPath,$dstPath);
// TODO: insert reference (i.e. $name) into database somehow...
And yes, researches have broken SHA-1, but you could easily write some safeguards against that if you're paranoid enough (e.g. check for an existing upload with the same hash; it the content differs, just mutate/append to the name a little to change it). You aren't identifying the image on the backend by its content: you just need a unique name. Once you have that, you just look it up from the DB to figure out the file path on disk corresponding to the actual image data.
If you expect the image files to be pretty large, you could limit how much you read into memory using the 4th parameter to file_get_contents().

PHP: Storing picture in the file directory, image name in db and retrieving an image

After doing research, I found that it is more recommended to save the image name in database and the actual image in a file directory. Two of the few reasons is that it is more safer and the pictures load a lot quicker. But I don't really get the point of doing this procedure because every time I retrieve the pictures with the firebug tool i can find out the picture path in the file directory which can lead to potential breach.
Am I doing this correctly or it is not suppose to show the complete file directory path of the image?
PHP for saving image into database
$images = retrieve_images();
insert_images_into_database($images);
function retrieve_images()
{
$images = explode(',', $_GET['i']);
return $images;
}
function insert_images_into_database($images)
{
if(!$images) //There were no images to return
return false;
$pdo = get_database_connection();
foreach($images as $image)
{
$path = Configuration::getUploadUrlPath('medium', 'target');
$sql = "INSERT INTO `urlImage` (`image_name`) VALUES ( ? )";
$prepared = $pdo->prepare($sql);
$prepared->execute(array($image));
echo ('<div><img src="'. $path . $image . '" /></div>');
}
}
One method to achieve what you originally intended to do by storing images in database is still continue to serve image via a PHP script, thus:
Shielding your users from knowing the actual path of an image.
You can, and should have, images stored outside of your DocumentRoot, so that they are not able to be served by web server.
Here's one way you can achieve that through readfile():
<?php
// image.php
// Translating file_id to image path and filename
$path = getPathFromFileID($_GET['file_id']);
$image = getImageNameFromFileID($_GET['file_id']);
// Actual full path to the image file
// Hopefully outside of DocumentRoot
$file = $path.$image;
if (userHasPermission()) {
readfile($file);
}
else {
// Better if you are actually outputting an image instead of echoing text
// So that the MIME type remains compatible
echo "You do not have the permission to load the image";
}
exit;
You can then serve the image by using standard HTML:
<img src="image.php?file_id=XXXXX">
You can use .htaccess to protect your images.
See here:
http://michael.theirwinfamily.net/articles/csshtml/protecting-images-using-php-and-htaccess
I'm also working on a project which stores the url path of images on the database (Amazon RDS) and the actual images in a cloud managed file system in Amazon S3.
The decision to do so came primarily with the concern of price, scalability and ease of implementation.
Cheaper: Firstly, it is cheaper to store data in a file system (Amazon S3) compared to a database (Amazon EC2 / RDS).
Scalable: And since the repository of images may grow pretty big in the future, you might also need to ensure that you have the adequate capacity to serve them. On this point, it is easier to scale up a filesystem compared to a database. In fact, if you are using cloud storage (like Amazon S3), you don't even need to worry about having not enough space as it has been managed for you by Amazon! you would just need to pay for what you use.
Ease of Implementation: In terms of implementation, storing images in a file system is much easier. If you were to serve images directly from databases, you would probably need to implement additional logic to convert blob files into html src blob strings to serve images. And from the look of it, this might actually take up quite substantial processing power which might slow your web server down.
On the other hand, if you were to use a filesystem, all you would require is to put down the url path of the image from the database to the src attribute of the image and its all done!
Security: As for security of the images, i have changed the image name to a timestamp concatenated with a random string so that it will prove really difficult for someone to browse for pictures without knowing the file name.
ie. 1342772480UexbblEY7Xj3Q4VtZ.png
Hope this helps!
NB: Please edit my post if you find anything wrong here! this is just my opinion and everyone is welcome to edit!

inserting/reading blob data from php

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 =)

PHP Upload to MySql

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.

How to store a PDF file in MySQL database?

How to store a PDF file in a MySQL database using PHP?
Using BLOB (Binary Large Object) (longblob datatype)
$fileHandle = fopen($fileUpload, "r");
$fileContent = fread($fileHandle, $fileUpload_size);
$fileContent = addslashes($fileContent);
$dbQuery = "INSERT INTO myBlobs VALUES ";
$dbQuery .= "('$fileContent')";
The full tutorial available here
but it's strongly recommended to store files on the file system, and just add a reference in the DB (a field with the file path and name). Several reasons:
Faster
Easier to access (don't need any special application)
Faster backups
Less space
Use a type BLOB.
Here's an example in PHP
As others mentioned, you can use a BLOB type.
Alternatively, what you can also do is save the PDF in the file system and save the relative link to it into the database. This is the same principle that applies for saving things such as Thumbnails and other data types that may be unique to the user and are expensive in terms of resources.
If you are simply looking to store uploaded PDF files on your server, it's more common to copy the file to a given directory with a unique filename and simply store the full path of that file in your MySQL table.
If you are definitely looking to store the full binary data of the file in your MySQL database, then you will have to do a little more work to put the binary data into a BLOB field in MySQL and then to turn it back into a file when you pull it out again at a later date.
You will probably want to store not only the binary data for the file in your MySQL table, but also the filename, filetype and possibly even the size of the file (for listing on your webpage). You will probably want a table such as;
CREATE TABLE files (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
filename VARCHAR(128) NOT NULL,
mimetype VARCHAR(64) NOT NULL,
data MEDIUMBLOB NOT NULL
);
In your PHP, you can then insert an uploaded file with something like the following;
$filePointer = fopen($_FILES['fileUpload']['tmp_name'], 'r');
$fileData = fread($filePointer, filesize($_FILES['fileUpload']['tmp_name']));
$fileData = addslashes($fileData);
$sql = "INSERT INTO files (filename, mimetype, data) VALUES( $fileName, $fileType, $fileData )";
Getting the file back out again will required a dedicated script that selects the appropriate file and then uses a series of 'header' commands to push that file back down to the browser with in a form that the browser knows how to handle it.
You can read a full tutorial about this here.

Categories