I'm having some trouble with this one. I have found some helpful scripts on the web and have been modifying them for my needs. However, I can't seem to download a file. It will respond back with the contents of the file but doesn't download it. I am using Polymer 1.0+ for my client side and PHP for my server side. The client side code to download a file is as follows:
<!--THIS IS THE HTML SIDE-->
<iron-ajax
id="ajaxDownloadItem"
url="../../../dropFilesBackend/index.php/main/DownloadItem"
method="GET"
handle-as="document"
last-response="{{downloadResponse}}"
on-response="ajaxDownloadItemResponse">
</iron-ajax>
//THIS IS THE JAVASCRIPT THAT WILL CALL THE "iron-ajax" ELEMENT
downloadItem:function(e){
this.$.ajaxDownloadItem.params = {"FILENAME":this.selectedItem.FILENAME,
"PATH":this.folder};
this.$.ajaxDownloadItem.generateRequest();
},
The server side code is as follows (the url is different because I do some url modification to get to the correct script):
function actionDownloadItem(){
valRequestMethodGet();
$username = $_SESSION['USERNAME'];
if(validateLoggedIn($username)){
$itemName = arrayGet($_GET,"FILENAME");
$path = arrayGet($_GET,"PATH");
$username = $_SESSION['USERNAME'];
$downloadItem = CoreFilePath();
$downloadItem .= "/".$_SESSION['USERNAME']."".$path."".$itemName;
DownloadFile($downloadItem);
}
else {
echo "Not Logged In.";
}
}
function DownloadFile($filePath) {
//ignore_user_abort(true);
set_time_limit(0); // disable the time limit for this script
//touch($filePath);
//chmod($filePath, 0775);
if ($fd = fopen($filePath, "r")) {
$fsize = filesize($filePath);//this returns 12
$path_parts = pathinfo($filePath);//basename = textfile.txt
$ext = strtolower($path_parts["extension"]);//this returns txt
$header = headerMimeType($ext); //this returns text/plain
header('Content-disposition: attachment; filename="'.$path_parts["basename"].'"'); // use 'attachment' to force a file download
header("Content-type: $header");
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
}
fclose ($fd);
}
Any help on this one would be greatly appreciated.
First you will need the file handle
$pathToSave = '/home/something/something.txt';
$writeHandle = fopen($pathToSave, 'wb');
Then, while you are reading the download, write to the file instead of echoing
fwrite($writeHandle, fread($fd, 2048));
Finally, after writing to the file finished close the handle
fclose($writeHandle);
I neglect the error check, you should implement your own.
Related
I am getting response from cURL which is an encoded string format of pdf(i guess) - see attached image.
I am getting pdf file if i directly put the url in browser. Since its asking for api credentials for viewing the pdf, its not user friendly and i am looking for another way to directly download the file.
So i am trying to get the string content of the pdf file with cURL and i am getting exactly what in the image attached(only small portion attached).
Using that string content, i tried to save it as a plain txt file and from there i tried to decode the string text and to create a pdf file newly. After creating i need to download the same.
I could create text file with the string data i got from cURL and could create pdf also. But the downloaded file showing Failed to load PDF document.
Below is the code i have tried and i am not sure whether i tried correctly or not.
function pdf_download(){
$this->load->helper('file');
$curl = $this->api_call->callapi('GET',APIURL."carts/96171/tickets");
$content = $curl;
$my_file = FCPATH . '/document/text.txt';
if (write_file($my_file, $content) == FALSE)
{
echo 'Unable to write the file';
}
else
{
echo 'File written!';
}
$pdf_base64 = $my_file;
//Get File content from txt file
$pdf_base64_handler = fopen($pdf_base64,'r');
$pdf_content = fread ($pdf_base64_handler,filesize($pdf_base64));
fclose ($pdf_base64_handler);
//Decode pdf content
$pdf_decoded = base64_decode ($pdf_content);
//Write data back to pdf file
$pdf_file=FCPATH . '/document/ticket.pdf';
$pdf = fopen ($pdf_file,'w');
fwrite ($pdf,$pdf_decoded);//Creating a pdf from the encoded content in txt file
fclose ($pdf);
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=tickets.pdf");
ob_clean(); flush();
readfile($pdf_file);//downloading the pdf file
exit();
}
Since streaming a download to the browser requires sending headers, you must not output anything before sending headers. Therefore, you must get rid of:
if (write_file($my_file, $content) == FALSE)
{
echo 'Unable to write the file';
}
else
{
echo 'File written!';
}
If you definitely need to check if the file was written (for debugging purposes, I assume) you may use CI's log_message() to write a debug entry in the logs or just set a control variable. For example:
if (write_file($my_file, $content) == FALSE)
{
log_message('debug', "Unable to write file");
$file_written = false;
}
else
{
log_message('debug', "File succesfully written");
$file_written = true;
}
Also, there's some headers missing to ensure the file is not streamed to the browser but sent for download, as well as some required to make sure the file is correctly downloaded
header('Content-Description: File Transfer');
header('Content-Type: '. mime_content_type($pdf_file)); // since you're using Codeigniter, this will try to use the correct mimetype
header('Content-Disposition: attachment; filename="ticket.pdf"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($pdf_file));
readfile($pdf_file);
The wrong line in the above given code is $pdf_decoded = base64_decode ($pdf_content);.
It should be changed to $pdf_decoded = $pdf_content;
Since string in encoded format is already getting which is needed for creating PDF. So no need to decode it.
Also the above code is reduced as below.
function pdf_download(){
$this->load->helper('file');
$this->load->helper('download');
$cart_id=$this->input->get('cart_id');
$curl = $this->api_call->callapi('GET',APIURL."cart/'.$cart_id.'/tickets");
$pdf_file=FCPATH . 'document\voucher-'.$cart_id.'.pdf';
if (write_file($pdf_file, $curl))
{
force_download($pdf_file, NULL);
}
}
I need to log total downloads of an specific file. Download function is working fine, but can't define if user canceled (clicking "cancel" on browser dialog) or if connection was aborted latter.
I understand it's not simple to know when a file download was finished, so I'm trying to get this by two ways. None works:
Get total bytes sent, latter I will compare it with total file size: this way $bytes_sent var always is set with total file size, no matter if user click cancel button of download dialog or if cancel download process latter.
Trigger connection_aborted() function: Have not found the way this function happen and define my session var...
(I'm not shure if the fact Im working with sessions is relevant).
I appreciate your help :)
<?php
if(is_file($filepath)){
$handle = fopen($filepath, "r");
header("Content-Type: $mime_type");
header("Content-Length: ". filesize($filepath).";");
header("Content-disposition: attachment; filename=" . $name);
while(!feof($handle)){
ignore_user_abort(true);
set_time_limit(0);
$data = fread($handle, filesize($filepath));
print $data;
$_SESSION['download'] = 'Successful download';
//Always is set as total file lenght, even when cancel a large file download before it finish:
bytes_sent = ftell($handle);
flush();
ob_flush();
//Can't trigger connection aborted, in any case:
if(connection_aborted()){
$_SESSION['download'] = 'Canceled download';
}
}
}
PHP Version 5.3.29
You need to read the file in small chunks, rather than reading it all in at once.
$chunk_size = 1000;
ignore_user_abort();
$canceled = false;
while ($chunk = fread($handle, $chunk_size)) {
print $chunk;
ob_flush();
$bytes_sent += strlen($chunk);
if (connection_aborted()) {
$canceled = true;
break;
}
}
$_SESSION['download'] = $canceled ? "Download canceled" : "Download successful";
I have an issue with displaying an image stored on my debian server outside the path accessible to the webserver in order to keep it safe. Here's a link to the expected results, as well as the relevant PHP code below:
header("content-type: image/png");
if (isset($_POST['mot_de_passe']) AND $_POST['mot_de_passe'] == "superPassword") {
// If password is valid
$filename = "/home/image/test.png";
$handle = fopen($filename, "rb");
$contents = fread($handle, filesize($filename));
fclose($handle);
echo $contents;
} else {
// If password is invalid
echo '<p>Mot de passe incorrect</p>';
}
This below statement should be first line of your script. Means before any html output. That solves the issue
header("content-type: image/png");
Try to use r+
fopen($filename, "r+");
Because you say Debian I have the question: PHP and httpd have access to read that directory(somewhere in my debian) and file (image)?
chmod
I tried a php solution but was told in my last question I "can't read/list a directory over HTTP. You'll need to use a different protocol to list a directory over the internet: FTP, SSH, etc. You'll need access to the remote server to do this. If the only thing you can use is HTTP, you'll need to retrieve the webpage (= the HTML document) and parse it yourself." ( How to get a php script to print/work )
I would like it to be simple - I can do HTML, I am learning Java, and I am just floundering around with PHP.
Update: an example. file.txt, file2.txt, & file3.txt are in /some/directory - I want a PHP script to grab one RANDOMLY & give it to me in a way I can put it in the href of an element. If not PHP, something else? thanks.
I haven't tested this.
$filenames=glob("files/*.txt");
$count=count($filenames);
if($count>0)
{
$rndfile=$filenames[rand(0,$count-1)];
echo '<a href="' . $rndfile . "'>Random file</a>";
}
First you need the files:
<?php
[...]
$files = scandir(dirname(__FILE__));
$links = array();
foreach($files as $file)
{
links[] = '<a href="http://localhost/url_decoder?file="'.md5_file($filepath.$file).'"> $file';
}
<table>
foreach ($links as $link){ echo "<tr><td>$link</td></tr>";}
<table>
?>
This little script scans the filepath of the current php script and creates one link per file in that directory to another php script that uses the file md5 hashed value as a parameter. Then the decoder should fo the following:
<?php
[...]
$file_search = $_GET['file'];
$files = scandir(dirname(__FILE__));//coder and decored located in same folder
foreach($files as $file)
{
if (md5_file(dirname(__FILE__).DIRECTORY_SEPARATOR.$file) == $file)
{
$fd = fopen (dirname(__FILE__).DIRECTORY_SEPARATOR.$file, "r")) {
$fsize = filesize(dirname(__FILE__).DIRECTORY_SEPARATOR.$file);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // use 'attachment' to force a download
break;
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
}
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
fclose ($fd);
exit;
}
}
?>
This is not perfect, and some of the code was taken from this page but at least gives you an idea how to operate. Hope this helps!
I'm developing a quick rapidshare-like site where the user can download files. First, I created a quick test setting headers and using readfile() but then I found in the comments section there's a way to limit the speed of the download, which is great, here's the code:
$local_file = 'file.zip';
$download_file = 'name.zip';
// set the download rate limit (=> 20,5 kb/s)
$download_rate = 20.5;
if(file_exists($local_file) && is_file($local_file))
{
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($local_file));
header('Content-Disposition: filename='.$download_file);
flush();
$file = fopen($local_file, "r");
while(!feof($file))
{
// send the current file part to the browser
print fread($file, round($download_rate * 1024));
// flush the content to the browser
flush();
// sleep one second
sleep(1);
}
fclose($file);}
else {
die('Error: The file '.$local_file.' does not exist!');
}
But now my question is, how to limit the number of downloads at the same time? How can I check there's still a connection with some user's IP?
Thanks.
Does a user have a login? if not just use sessions, or even better track on their ip-address.
Here's a sessions example:
$_SESSION['file_downloading']==true;
$file = fopen($local_file, "r");
while(!feof($file))
{
// send the current file part to the browser
print fread($file, round($download_rate * 1024));
// flush the content to the browser
flush();
// sleep one second
sleep(1);
}
$_SESSION['file_downloading']=null;
fclose($file);}
Then above all this code,
if(!empty($_SESSION['file_downloading']))
//perform a redirect or reduce their download rate or something.
Next option is via ip address.
//http://wiki.jumba.com.au/wiki/PHP_Get_user_IP_Address
function VisitorIP()
{
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$TheIp=$_SERVER['HTTP_X_FORWARDED_FOR'];
else $TheIp=$_SERVER['REMOTE_ADDR'];
return trim($TheIp);
}
get the visitor ip address, store this in the database along with the datetime stamp. Then simply remove that ip address when the file is finished downloading. Are you using a database system?