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?
Related
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 am downloading a file from Ftp folder after upload.
Problem is when i open the txt file it show s html page source appending with file content
If i preview image file(jpg or jpeg) it shows image is corrupted
If i open pdf error: Failed to load Pdf document
Please let me know where i am wrong.
Here is my code:
if (isset($_GET['id']) && basename($_GET['id']) == $_GET['id']) {
$filename = $_GET['id'];
} else {
$filename =NULL ;
}
$err = 'Sorry, the file you are requesting is unavailable.';
if (!$filename) {
// if variable $filename is NULL or false display the message
echo $err;
} else {
// define the path to your download folder plus assign the file name
$path = '/home/devestctrl/public_html/wp-content/uploads/'.$filename;
// check that file exists and is readable
if (file_exists($path) && is_readable($path)) {
// get the file size and send the http headers
$size = filesize($path);
header('Content-Type: application/octet-stream');
header('Content-Type: '.$mime);
header('Content-Length: '.$size);
header('Content-Disposition: attachment; filename='.$filename);
header('Content-Transfer-Encoding: binary');
// open the file in binary read-only mode
// display the error messages if the file canĀ“t be opened
$file = # fopen($path, 'rb');
if ($file) {
// stream the file and exit the script when complete
fpassthru($file);
exit();
} else {
echo $err;
}
} else {
echo $err;
}
}
I checked by using echo of $filename;
It shows the output in the file instead of printing in page.
error displays:
Sorry, the file you are requesting is unavailable.
After error also i can download, but echoed $filename displayed in file.
Insert into table:
echo "<tr><a href='?id=" . $row["FileupName"]. "'>".$row["FileupName"]."</td></tr>";
You cannot output data to a page and download a file at the same time. One request from your browser will let you do one response. This response is normally displayed in the browser as a page, unless you set the download headers, then the browser will ask you to download the response to a file.
So what you'll need to do is have two requests and responses: In the first, you output your page and a link the user needs to click (or JavaScript or a refresh meta tag to do it automatically) to download the file in a second request.
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.
Here is a simple script I have written to limit downloads for users to one at a time (IE if they are downloading a file then they cannot download another one until they cancel the current download or it finishes).
ignore_user_abort(true);
$local_file = $_GET['filename'];
$download_file = explode("/", $local_file);
$download_file = $download_file[count($download_file) -1];
// set the download rate limit (value is in kilobytes per second
$download_rate = 100;
if(file_exists($local_file) && is_file($local_file)) {
$ip = visitor_ip();
if(!are_downloading($ip)) {
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");
log_downloader($ip);
while(!feof($file)) {
if (!connection_aborted()) {
// 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);
} else {
break;
}
}
clear_downloader($ip);
fclose($file);
} else {
die('<span style="color:#DDDDDD">Due to server limitations you may only download one file at a time. Please cancel or wait for your current download to finish before trying again. Click here to return.</span>');
}
} else {
die('Error: The file '.$local_file.' does not exist!');
}
function visitor_ip() {
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$TheIp=$_SERVER['HTTP_X_FORWARDED_FOR'];
else $TheIp=$_SERVER['REMOTE_ADDR'];
return trim($TheIp);
}
function are_downloading($ip) {
$query = "select * from downloaders where ip_addr='$ip'";
$result = mysql_query($query);
$num_rows = mysql_num_rows($result);
return $num_rows > 0;
}
function log_downloader($ip) {
$query = "insert into downloaders (ip_addr) values ('$ip')";
$result = mysql_query($query);
}
function clear_downloader($ip) {
$query = "delete from downloaders where ip_addr='$ip'";
$result = mysql_query($query);
}
When I test it out, it works fine, but for a lot of people, their IP never gets cleared out of the database - even when they have finished downloading/cancelled a file. Why don't the IPs get deleted?
The problem was that with big downloads the MySQL connection went away, I simply had to reconnect in the clear_downloader function and now it works fine.
I am using microsoft tag php library by Scott Vanderbeck.
It has a function to output the barcode to browser as an image to browser, but I would like download and save to disk. My goal is to loop through all the tags and download each barcode as an image onto a disk. I am not sure how to accomplish this.
Here is my code
require_once('MSTag_v2.php');
$MSTagAuthToken = "your token";
//Create an MSTag interface instance
$msTag = new MSTag();
//Create User Credentials
$userCredential = new UserCredential($MSTagAuthToken);
//Display Microsoft Tag image in browser
$result = $msTag->GetBarcode($userCredential,'MAIN','Cyclamen coum Pewter','jpeg',1);
if($result)
{
ob_start();
$length = strlen($result);
header('Last-Modified: '.date('r'));
header('Accept-Ranges: bytes');
header('Content-Length: '.$length);
header('Content-Type: image/jpeg');
print($result);
ob_end_flush();
exit;
}
else
{
echo $msTag->getLastException();
}
You could save the images directly to disk
if($result)
{
file_put_contents($filename, $result);
}
Just generate a filename for each so you do not overwrite them (maybe use tempnam()).