PHP batch extract DB row BLOBs to files - php

We are in the midst of a data migration , and the project needs from one particular table BLOB data saved to files, and sent to the vendor, due to their structure
I am able to achieve this one at a time, but needed to create a batch process, as there are 50,000 rows/potential files.
my current code is
$sql = "SELECT a.guid AS file_name, a.attachment AS file_blob"
." FROM attachment a";
$squery = oci_parse($link, $sql);
oci_execute($squery);
while ($row = oci_fetch_array($squery, OCI_ASSOC | OCI_RETURN_LOBS)) {
header('Content-type: octet-stream;');
header('Content-disposition: attachment;filename='.$row['FILE_NAME']);
print $row['FILE_BLOB'];
}
oci_free_statement($squery);
I do understand that I would rather be saving the data rather than streaming it to the browser, I just am not wrapping my head around how to achieve this, should i be looking at php file I/O fputs()?
End result is I would like the process to batch write binary files based on the query to a folder
EDIT
Thank you for your direction and help, based on that I ended up coming up with two separate ways to achieve this
first i did the following getcwd() to verify where the server was pointing to, and did an absolute path if needed
$basedir = '/path/to/host/www/blobdoc/';
set permissions on the blobdoc folder then used either of the following scripts in the while loop
$filename='';
$filename=$basedir.$row['FILE_NAME'];
file_put_contents( $filename, $row['FILE_BLOB']);
$filename='';
$filename=$basedir.$row['FILE_NAME'];
$File = #fopen( $filename, 'w' );
if( $File ) {
if( FALSE === fwrite( $File, $row['FILE_BLOB'] ))
return FALSE;
fclose( $File );
return TRUE;
}

Instead of:
print $row['FILE_BLOB'];
Use something like:
file_put_contents( $filename, $row['FILE_BLOB']); //save locally
You need to write the blob to a file.
If you want to force a download of that file then you need to make use of the correct headers in combinarion with readfile, like so:
$file = '/var/www/html/file-to-download.zip';
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Length: ' . filesize($filename));
header('Content-Disposition: attachment; filename=' . basename($file));
readfile($file);

Related

My downloaded file is changing its name to 'download' although I changed it in the headers

I'm trying to make 2 option names to download a file: Dictionary and Random.
When the user selects the dictionary option it downloads the file constantly with the name 'download' instead of some random word from the dictionary and the random option works fine.
I tried using the load function and printing what it returns and it does print the actual randomly picked word from the dictionary but it doesn't in the download part. I'm guessing its something with the headers but it might not.
That's the load function which loads the dictionary from a file on the server:
function load($file) {
$file_arr = file($file);
$num_lines = count($file_arr);
$last_arr_index = $num_lines - 1;
$rand_index = rand(0, $last_arr_index);
$rand_text = $file_arr[$rand_index];
return $rand_text;
}
That's the download function which downloads the file:
function download($file, $filename) {
header("Content-Type: application/jpeg");
header("Content-Length: " . filesize($file));
header('Content-Disposition: attachment; filename="' . $filename . '"');
readfile($file);
exit();
}
That's how I call the functions:
$filename = load("includes/dictionary_words.txt");
download($file, $filename);
Lastly the $file variable:
$file = "main/app.exe";
Instead of downloading the file with a random dictionary name it downloads it with a 'download' name. I didn't get any errors or notices.

php saving an image to postgresql and read it back is not working

This must be common asked question, But I could not find from google. We have a table and has photo column in bytea type in postgresql.
We are saving upcoming image with this snippet:
$photo->attributes = $_FILES['Photo'];
$file = CUploadedFile::getInstance($photo, 'photo');
$path = Yii::app()->basePath . '/data/' . $file->name;
$file->saveAs($path); //save the file to the $path
$fp = fopen($path, 'rb');
$content = fread($fp, filesize($path)); //get the file content
fclose($fp);
$photo->photo = base64_encode($content); //encode it
$photo->save(); //save the record to db
unlink(Yii::app()->basePath . '/data/' . $file->name);
saving seems working good.
this is where we are reading blob field from db:
base64_decode($data->photo) //this call is giving base64_decode() expects parameter 1 to be string, resource given error.
if I do:
print_r($data->photo) //I am getting: Resource id #51
Obviously $data->photo is not binary string, it is coming as a resource. Any idea how to make it work?
thanks in advance.
If you're using base64 then you don't need bytea, you can use a regular text field. It'll be less efficient for disk space, but that's about it.
If you want to store the actual bytes, you don't encode it as base64, you use pg_escape_bytea to convert it to PostgreSQL's hex-string representation (for modern PostgreSQL versions) of bytea values. You use pg_bytea_decode when extracting the data.
If you're using PDO instead of the native PostgreSQL driver, look up how to work with bytea using PDO. You haven't actually shown your database code at all, only some wrapper for it, so it's very hard to tell what's going on.
Of course, all this only applies to PHP; most languages have separate data types for "textual data" and "binary data" that let the client library automatically convert binary data without you having to jump through escape/unescape hooks.
For me work this solution:
Save file to DB:
$file = CUploadedFile::getInstanceByName('file');
if ($file!=null) {
$fp = fopen($file->tempName,'r');
$content = fread($fp, $file->size);
fclose($fp);
$doc->doc=null; //bytea field
$doc->docname = $file->name;
$doc->mime = $file->type;
$doc->size = $file->size;
$doc->save();
$msg = $doc->getErrors();
$command = Yii::app()->db->createCommand('UPDATE '.
$doc->tableName().' SET "doc"=:doc WHERE id='.$doc->id);
$command->bindParam(":doc", $content, PDO::PARAM_LOB); // <-- look this!
$command->execute();
}
Show file from DB:
header('Content-Type: '.$doc->mime );
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ($doc->docname ) );
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $doc->size);
$content = fread($doc->doc, $doc->size);
echo $content;
exit;

Upload and save large file into database as a varbinary

I'm having a problem with PHP and my SQL Server.
When I try to save a file into the database as a varbinary(MAX) type, it works perfectly, but when I try to save a file of 180k or more in the result, I only get part of the image:
$handle = #fopen($fileTmpName, 'rb');
if ($handle)
{
$content = #fread($handle, filesize($fileTmpName));
$content = bin2hex($content);
#fclose($handle);
}
$select = mssql_query("EXEC [DB_Name].[dbo].[Table_Name] #Data = ".$content."");
$result = mssql_fetch_array($select);
$_SESSION['fileContent'] = $result['Data'];
<img src="../applications/framework/images.php" />
image.php file contains the following:
header("Content-type: ".$fileType);
header("Content-Length: ".$fileSize);
header("Content-Disposition: inline; filename=".$fileName);
echo $_SESSION['fileContent'];
As a result, I only get part of the image.
I mean that it only loads the first part of the real image. It seems that it inserts only the first part of the content into the database instead of all of it.
How can this be solved?
check the table structure. It might be the column might not be set up as a var(max)

retrieving files from database by their path

I have a simple html form, that saves the email message and multiple attachments path. I am saving the files on the server and their path to the database filed.
Now how can i retrive the files from its path, and then show them to user, when click on Download, I am using the following code for getting the file, but this is not working
$query = "
SELECT `type`, `name`, `size`, `file1`,`file2`,`file3`,`file4`,`file5`
FROM `upload` WHERE `id` = {$id}";
$result = $dbLink->query($query);
if($result) {
if($result->num_rows == 1) {
$row = mysqli_fetch_assoc($result);
header("Content-Type: ". $row['type']);
header("Content-Length: ". $row['size']);
header("Content-Disposition: attachment; filename=". $row['name']);
$path = $row['file1'];
$dir = opendir($path);
echo $dir;
while ($dir && ($file = readdir($dir)) !== false) {
echo $file;
}
The echoed file doesnot contain any data.
This i am doing now for only one file, whose path is at "file1". Similarly i have 5 attachments path, and i have to retrive them all in this code.
Please how can i do it.
First: You can't have more than one download like this. You can only send that header once and that's it.
Is $path the full path to the file? You could try is_readable($file) to check first in a test. Once you have the full path though, it's simple.
header("Content-Type: application/octet-stream");
header("Content-Length: ". $row['size']);
header("Content-Disposition: attachment; filename=". $row['name']);
readfile($row['file1']);
exit();
Readfile outputs directly and streams it, so you don't have to read the content in and then output it. Add the exit to make sure no other output get's added after it, or you could corrupt the file.
This works, assuming $row['file1'] has a correct path!
For multiple files, you have to make multiple calls. Like give the user mutliple links they can click on for downloads.
The only alternative is that you pack it together using zip for example, and send it as one large zipfile.

show all files in a directory on a page [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
PHP list all files in directory
ive got a cron job running creating .xls files every 24hrs, they are creating them in a directory on my site called www.mysite.com/sheets
what i want to do is make a webpage where i can go to and see all the .xls files in that directory and download the ones i was instead of having to download them using an ftp client.
Is there a name for this sort of thing ? what would i write it in ? i was thinking i could do it in php by echoing the folders contents, would that work ?
cheers
You could activate directory listing within your webserver, or you can use a function like glob("*.xls") to list the files and echo their path on the webserver or you can send a file to the browser like this:
<?php
$filename = "path/to/your/file.xls";
$real_filename = "file.xls";
header('Content-Transfer-Encoding: none');
header('Content-Type: application/octet-stream; name="' . $real_filename . '"');
header('Content-Disposition: attachment; filename=' . $real_filename);
$size = #filesize($filename);
if ($size > 0) {
header('Content-length: '.$size);
} else {
header('Content-length: '.#strlen(#file_get_contents($filename)));
}
readfile($filename);
exit;
?>
If you want to list a directory use this:
$handle = opendir("./");
while (false !== ($file = readdir($handle))) {
$fileinfo = pathinfo($file);
if (strtolower($fileinfo[extension]) == "xls") {
echo $file;
}
}
closedir($handle);
Or this:
foreach (glob("*.[xX][lL][sS]") as $filename) {
echo $filename;
}
Use glob() plus some filtering for xls files.
glob("*.xls")
Potentially useful resources (in order of preference):
Manual
http://php.net/filesystemiterator
http://php.net/glob
http://php.net/scandir
http://php.net/opendir
http://php.net/function.dir

Categories