I have this download script which works perfectly, but one the file is downloaded, I want the page to reload. But once the file is downloaded, the page just dies? It is supposed to be a secure download script so users can't see the paths of files. It's called by using a POST submission
<?php
session_start();
//Assign variables from session
$userid = $_SESSION["id"];
$email = $_SESSION['email'];
$password = $_SESSION['password'];
//Require database connection and functions
require('includes/config.php');
include('includes/functions.php');
//Check if user is logged in
if (!$userid || !$email || !$password)
{
header("Location: index.php");
}
// Usage: Download
// Path to downloadable files (will not be revealed to users so they will never know your file's real address)
$hiddenPath = "uploaded_notes/";
// VARIABLES
if (!empty($_POST['file'])){
$file_id = $_POST['file'];
$result = mysql_query("SELECT * FROM files WHERE id='$file_id'");
$row = mysql_fetch_array($result);
$file = $row['location'];
$price = $row['price'];
$result2 = mysql_query("SELECT coins FROM users WHERE id='$userid'");
$row2 = mysql_fetch_array($result2);
$coins = $row2['coins'];
$new_coins = $coins-$price;
if ($new_coins >= 0)
{
mysql_query("UPDATE users SET coins = $new_coins WHERE id='$id'");
}
else {
header("Location: dashboard.php");
die();
}
//Insert into purchases
$datetime = date("Y-m-d H:i:s");#
mysql_query("INSERT INTO purchases (userid, fileid, datetime) VALUES ('$userid', '$file_id', '$datetime')");
$file = str_replace('%20', ' ', $file);
$category = (!empty($_GET['category'])) ? $_GET['category'] . '/' : '';
} else {
header('Location: dashboard.php');
die('Hacking attempt reported.');
}
$file_real = $hiddenPath . $category . $file;
$ip = $_SERVER['REMOTE_ADDR'];
// If requested file exists
if (file_exists($file_real)){
// Get extension of requested file
$extension = strtolower(substr(strrchr($file, "."), 1));
// Determine correct MIME type
switch($extension){
case "asf": $type = "video/x-ms-asf"; break;
case "avi": $type = "video/x-msvideo"; break;
case "exe": $type = "application/octet-stream"; break;
case "mov": $type = "video/quicktime"; break;
case "mp3": $type = "audio/mpeg"; break;
case "mpg": $type = "video/mpeg"; break;
case "mpeg": $type = "video/mpeg"; break;
case "rar": $type = "encoding/x-compress"; break;
case "txt": $type = "text/plain"; break;
case "wav": $type = "audio/wav"; break;
case "wma": $type = "audio/x-ms-wma"; break;
case "wmv": $type = "video/x-ms-wmv"; break;
case "zip": $type = "application/x-zip-compressed"; break;
case "jpg": $type = "image/jpeg"; break;
default: $type = "application/force-download"; break;
}
// Fix IE bug [0]
$header_file = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? preg_replace('/\./', '%2e', $file, substr_count($file, '.') - 1) : $file;
// Prepare headers
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public", false);
header("Content-Description: File Transfer");
header("Content-Type: " . $type);
header("Accept-Ranges: bytes");
header("Content-Disposition: attachment; filename=\"" . $header_file . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($file_real));
// Send file for download
if ($stream = fopen($file_real, 'rb')){
while(!feof($stream) && connection_status() == 0){
//reset time limit for big files
print(fread($stream,1024*8));
flush();
}
fclose($stream);
}
}else{
// Requested file does not exist (File not found)
echo("Requested file does not exist");
die();
}
?>
The refresh code works if I put it right before
print(fread($stream,1024*8));
But after that it does not work...
Downloading a file is a one time thing. You won't be able to redirect after the file is downloaded. You can try adding a refresh header before pumping out your contents, but some browsers will see that as a cancellation.
See this post for more information: https://stackoverflow.com/questions/5960895/redirect-after-download-not-working-php
You can read this post, this post, and this post for more information.
Basically, what they all boil down to is the HTTP headers. A redirect header cannot be sent after you have echoed content. On the other hand, a redirect header before echoing content will just cancel the download.
A different way you could do this would be to have a hidden iframe, and submit your request there, but I am guessing that isn't what you are wanting.
Related
I have this script:
<?php
$id = $_GET["id"];
switch($id)
{
default: echo "Bad ID!"; exit;
case "MyPicture1": $file = "img/img1.jpg"; break;
case "MyPicture2": $file = "img/img2.jpg"; break;
case "MyPicture3": $file = "img/img3.jpg"; break;
case "MyPicture4": $file = "img/img4.jpg"; break;
}
if(file_exists($file))
{
$size = getimagesize($file);
$fp = fopen($file, 'rb');
if($size and $fp)
{
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($file)).' GMT');
header('Content-Type: '.$size['mime']);
header('Content-Length: '.filesize($file));
fpassthru($fp);
}
exit;
}
else
{
echo "Bad ID!";
}
?>
Which works fine for loading images like this <IMG Src="/Script.php?id=MyPicture1" />!
But the problem occurs when I try to load ANY other MIME content, to be displayed directly on the page or to switch page with it, but unsuccessfully... I tried with:
<?php
$file_name = $_GET["id"];
switch($file_name)
{
default: echo 'Bad ID!'; exit;
case "style1": $file_name = "csss/style_one.css"; break;
case "style2": $file_name = "csss/style_two.css"; break;
case "image1": $file_name = "images/image_one.jpg"; break;
case "image2": $file_name = "images/image_one.png"; break;
case "image3": $file_name = "images/image_one.bmp"; break;
case "image4": $file_name = "images/image_one.gif"; break;
case "video1": $file_name = "videos/video_one.avi"; break;
case "video2": $file_name = "videos/video_two.mp4"; break;
case "video3": $file_name = "videos/video_three.mov"; break;
case "php_page_1": $file_name = "php_pages/php_one.php"; break;
case "html_page_1": $file_name = "html_pages/html_one.html"; break;
case "icon_fav_1": $file_name = "icons/icon_one.ico"; break;
case "font_1": $file_name = "fonts/font_one.otf"; break;
}
if(file_exists($file_name))
{
$size = getImageSize("$file_name");
$fP = fOpen("$file_name", "rb");
if($size and $fP)
{
header('last-modified: '.gmdate('D, d M Y H:i:s', filemtime($file_name)).' GMT');
header('content-type: '.$size['mime'].');
header('content-length: '.filesize($file_name).);
fPassThru($fP);
}
exit;
}
else
{
echo 'Bad ID!';
}
?>
If I load CSS sheet like /Script.php?id=MyCSS1, I want CSS file to be loaded properly on the page - that is I want CSS file to be loaded on the page as if it was loaded with <LINK rel="stylesheet" type="text/css" href="css/styles_one.css" />. The same applies to ANY and ALL other MIME content, including switching pages with ANY extension with it - like /Script.php?id=php_page_1 (if php page 1 is clicked).
Can you help?
You can use the php function mime_content_type. Here is an example with some syntactic sugar.
<?php
$file_name = $_GET["id"];
switch($file_name)
{
default: echo 'Bad ID!'; exit;
case "style1": $file_name = "csss/style_one.css"; break;
case "style2": $file_name = "csss/style_two.css"; break;
case "image1": $file_name = "images/image_one.jpg"; break;
case "image2": $file_name = "images/image_one.png"; break;
case "image3": $file_name = "images/image_one.bmp"; break;
case "image4": $file_name = "images/image_one.gif"; break;
case "video1": $file_name = "videos/video_one.avi"; break;
case "video2": $file_name = "videos/video_two.mp4"; break;
case "video3": $file_name = "videos/video_three.mov"; break;
case "php_page_1": $file_name = "php_pages/php_one.php"; break;
case "html_page_1": $file_name = "html_pages/html_one.html"; break;
case "icon_fav_1": $file_name = "icons/icon_one.ico"; break;
case "font_1": $file_name = "fonts/font_one.otf"; break;
}
if(file_exists($file_name)) {
header("last-modified: {gmdate('D, d M Y H:i:s', filemtime($file_name))} GMT");
header("content-type: {mime_content_type($file_name)}");
header("content-length: {filesize($file_name)}");
readfile($file_name);
} else {
echo 'Bad ID!';
}
exit;
If you are using php 8. You will have to write it like this.
<?php
$file_name = $_GET["id"];
switch($file_name)
{
default: echo 'Bad ID!'; exit;
case "style1": $file_name = "csss/style_one.css"; break;
case "style2": $file_name = "csss/style_two.css"; break;
case "image1": $file_name = "images/image_one.jpg"; break;
case "image2": $file_name = "images/image_one.png"; break;
case "image3": $file_name = "images/image_one.bmp"; break;
case "image4": $file_name = "images/image_one.gif"; break;
case "video1": $file_name = "videos/video_one.avi"; break;
case "video2": $file_name = "videos/video_two.mp4"; break;
case "video3": $file_name = "videos/video_three.mov"; break;
case "php_page_1": $file_name = "php_pages/php_one.php"; break;
case "html_page_1": $file_name = "html_pages/html_one.html"; break;
case "icon_fav_1": $file_name = "icons/icon_one.ico"; break;
case "font_1": $file_name = "fonts/font_one.otf"; break;
}
if(file_exists($file_name)) {
$last_modified = gmdate('D, d M Y H:i:s', filemtime($file_name));
header("last-modified: $last_modified GMT");
$content_type = mime_content_type($file_name);
header("content-type: $content_type");
$content_length = filesize($file_name);
header("content-length: $content_length");
readfile($file_name);
} else {
echo 'Bad ID!';
}
exit;
getImageSize() is just for image files. The more general function for getting MIME types is mime_content_type().
if(file_exists($file_name))
{
$type = mime_content_type("$file_name");
$fP = fOpen("$file_name", "rb");
if($type and $fP)
{
header('last-modified: '.gmdate('D, d M Y H:i:s', filemtime($file_name)).' GMT');
header('content-type: '.$type.');
header('content-length: '.filesize($file_name).);
fPassThru($fP);
}
exit;
}
else
{
echo 'Bad ID!';
}
I'm using the latest version of mybb (mybb1.8), and it has attachment feature, but it upload the files into current host, I want upload the files to different server or host.
How can I do that ?
Download Mybb18 if you need it:
http://resources.mybb.com/downloads/mybb_1800.zip
PHP code of attachment.php file
<?php
/**
* MyBB 1.8
* Copyright 2014 MyBB Group, All Rights Reserved
*
* Website: http://www.mybb.com
* License: http://www.mybb.com/about/license
*
*/
define("IN_MYBB", 1);
define('THIS_SCRIPT', 'attachment.php');
require_once "./global.php";
if($mybb->settings['enableattachments'] != 1)
{
error($lang->attachments_disabled);
}
// Find the AID we're looking for
if(isset($mybb->input['thumbnail']))
{
$aid = $mybb->get_input('thumbnail', 1);
}
else
{
$aid = $mybb->get_input('aid', 1);
}
$pid = $mybb->get_input('pid', 1);
// Select attachment data from database
if($aid)
{
$query = $db->simple_select("attachments", "*", "aid='{$aid}'");
}
else
{
$query = $db->simple_select("attachments", "*", "pid='{$pid}'");
}
$attachment = $db->fetch_array($query);
$plugins->run_hooks("attachment_start");
if(!$attachment)
{
error($lang->error_invalidattachment);
}
$pid = $attachment['pid'];
// Don't check the permissions on preview
if($pid || $attachment['uid'] != $mybb->user['uid'])
{
$post = get_post($pid);
$thread = get_thread($post['tid']);
if(!$thread && !isset($mybb->input['thumbnail']))
{
error($lang->error_invalidthread);
}
$fid = $thread['fid'];
// Get forum info
$forum = get_forum($fid);
// Permissions
$forumpermissions = forum_permissions($fid);
if($forumpermissions['canview'] == 0 || $forumpermissions['canviewthreads'] == 0 || (isset($forumpermissions['canonlyviewownthreads']) && $forumpermissions['canonlyviewownthreads'] != 0 && $thread['uid'] != $mybb->user['uid']) || ($forumpermissions['candlattachments'] == 0 && !$mybb->input['thumbnail']))
{
error_no_permission();
}
// Error if attachment is invalid or not visible
if(!$attachment['attachname'] || (!is_moderator($fid, "canviewunapprove") && ($attachment['visible'] != 1 || $thread['visible'] != 1 || $post['visible'] != 1)))
{
error($lang->error_invalidattachment);
}
}
if(!isset($mybb->input['thumbnail'])) // Only increment the download count if this is not a thumbnail
{
$attachupdate = array(
"downloads" => $attachment['downloads']+1,
);
$db->update_query("attachments", $attachupdate, "aid='{$attachment['aid']}'");
}
// basename isn't UTF-8 safe. This is a workaround.
$attachment['filename'] = ltrim(basename(' '.$attachment['filename']));
$plugins->run_hooks("attachment_end");
if(isset($mybb->input['thumbnail']))
{
$ext = get_extension($attachment['thumbnail']);
switch($ext)
{
case "gif":
$type = "image/gif";
break;
case "bmp":
$type = "image/bmp";
break;
case "png":
$type = "image/png";
break;
case "jpg":
case "jpeg":
case "jpe":
$type = "image/jpeg";
break;
default:
$type = "image/unknown";
break;
}
header("Content-disposition: filename=\"{$attachment['filename']}\"");
header("Content-type: ".$type);
$thumb = $mybb->settings['uploadspath']."/".$attachment['thumbnail'];
header("Content-length: ".#filesize($thumb));
$handle = fopen($thumb, 'rb');
while(!feof($handle))
{
echo fread($handle, 8192);
}
fclose($handle);
}
else
{
$ext = get_extension($attachment['filename']);
switch($attachment['filetype'])
{
case "application/pdf":
case "image/bmp":
case "image/gif":
case "image/jpeg":
case "image/pjpeg":
case "image/png":
case "text/plain":
header("Content-type: {$attachment['filetype']}");
$disposition = "inline";
break;
default:
$filetype = $attachment['filetype'];
if(!$filetype)
{
$filetype = 'application/force-download';
}
header("Content-type: {$filetype}");
$disposition = "attachment";
}
if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']), "msie") !== false)
{
header("Content-disposition: attachment; filename=\"{$attachment['filename']}\"");
}
else
{
header("Content-disposition: {$disposition}; filename=\"{$attachment['filename']}\"");
}
if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']), "msie 6.0") !== false)
{
header("Expires: -1");
}
header("Content-length: {$attachment['filesize']}");
header("Content-range: bytes=0-".($attachment['filesize']-1)."/".$attachment['filesize']);
$handle = fopen($mybb->settings['uploadspath']."/".$attachment['attachname'], 'rb');
while(!feof($handle))
{
echo fread($handle, 8192);
}
fclose($handle);
}
Firstof: a Form that posts(Data, a file) do a server it has not been called from ist considered a security risk. It is not so easy to implement so that browsers feel confident. So your recieving script should be on the same server.
That is why I would move the files after they have been posted.
I would use the command line for that if you have a linux/unix server.
You will have to copy the file from your host onto the target and delete the orginal file when successful.
Either in the filesystem(on the same server) via
system("cp filename targetdir");
or onto a different server via
system("scp filename server:targetdir")
also have a look at "rsync" which ist much more efficient when syncing files from server to server.
I use PHP to send download to the browser. I run the code on a file named download.php.
The problem: sometimes instead of download the file, it's downloads the same file (same size), but with the name download.php.
I also noticed that when I try to download it with Internet Download Manager, I see the name download.php for about half second, and then the name changes to the real name.
Pictures to explain:
The code:
//First, see if the file exists
if (!is_file($file)) {
header("HTTP/1.1 400 Invalid Request");
die("<h3>File Not Found</h3>");
}
//Gather relevent info about file
$size = filesize($file);
$fileinfo = pathinfo($file);
//workaround for IE filename bug with multiple periods / multiple dots in filename
//that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe
$filename = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ?
preg_replace('/\./', '%2e', $fileinfo['basename'], substr_count($fileinfo['basename'], '.') - 1) :
$fileinfo['basename'];
$file_extension = strtolower($fileinfo['extension']);
//This will set the Content-Type to the appropriate setting for the file
switch($file_extension)
{
case 'exe': $ctype='application/octet-stream'; break;
case 'zip': $ctype='application/zip'; break;
case 'mp3': $ctype='audio/mpeg'; break;
case 'mpg': $ctype='video/mpeg'; break;
case 'avi': $ctype='video/x-msvideo'; break;
default: $ctype='application/force-download';
}
//check if http_range is sent by browser (or download manager)
if($is_resume && isset($_SERVER['HTTP_RANGE']))
{
$arr = explode('=', $_SERVER['HTTP_RANGE'], 2);
if(isset($arr[1]))
list($size_unit, $range_orig) = $arr;
else list($size_unit) = $arr;
if ($size_unit == 'bytes')
{
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
$arr3 = explode(',', $range_orig, 2);
if(isset($arr3[1]))
list($range, $extra_ranges) = $arr3;
else
list($range) = $arr3;
}
else
{
$range = '';
}
}
else
{
$range = '';
}
//figure out download piece from range (if set)
$arr2 = explode('-', $range, 2);
if(isset($arr2[1]))
list($seek_start, $seek_end) = $arr2;
else
list($seek_start) = $arr2;
//set start and end based on range (if set), else set defaults
//also check for invalid ranges.
$seek_end = (empty($seek_end)) ? ($size - 1) : min(abs(intval($seek_end)),($size - 1));
$seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);
//add headers if resumable
if ($is_resume)
{
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seek_start > 0 || $seek_end < ($size - 1))
{
header('HTTP/1.1 206 Partial Content');
}
header('Accept-Ranges: bytes');
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$size);
}
//headers for IE Bugs (is this necessary?)
//header("Cache-Control: cache, must-revalidate");
//header("Pragma: public");
header('Content-Type: ' . $ctype);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: '.($seek_end - $seek_start + 1));
//reset time limit for big files
set_time_limit(0);
ignore_user_abort(true);
//open the file
$fp = fopen($file, 'rb');
//seek to start of missing part
fseek($fp, $seek_start);
//start buffered download
while(!feof($fp))
{
print(fread($fp, 1024*8));
flush();
ob_flush();
}
fclose($fp);
exit;
I found the solution!
I did an htaccess RewriteRule. For example, if the file id is a1a2, and the file name is foo.mp4, I changed the URL to: download/a1a2/foo.mp4.
htaccess code:
RewriteEngine on
RewriteRule ^download/(.*)/(.*)?$ download.php?id=$1&fileName=$2 [QSA,NC,L]
That simple!
You can do this by setting proper headers as explained in the official PHP manual here:
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
That said looking at your code it seems like you already the header stuff covered here:
header('Content-Type: ' . $ctype);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: '.($seek_end - $seek_start + 1));
So the only thing I think could be the issue is related to the list mime-types you have in your code:
//This will set the Content-Type to the appropriate setting for the file
switch($file_extension)
{
case 'exe': $ctype='application/octet-stream'; break;
case 'zip': $ctype='application/zip'; break;
case 'mp3': $ctype='audio/mpeg'; break;
case 'mpg': $ctype='video/mpeg'; break;
case 'avi': $ctype='video/x-msvideo'; break;
default: $ctype='application/force-download';
}
It could be that the mime-type (aka in your code as: $ctype) you are setting is not recognized by the browser or the IDM download software you are using. So it is defaulting to application/force-download.
I would also highly recommend refactoring your switch to work like this using simple associative array logic like this:
// This will set the Content-Type to the appropriate setting for the file
$ctype_array = array();
$ctype_array['exe'] = 'application/octet-stream';
$ctype_array['zip'] = 'application/zip';
$ctype_array['mp3'] = 'audio/mpeg';
$ctype_array['mpg'] = 'video/mpeg';
$ctype_array['avi'] = 'video/x-msvideo';
// Check if the file extension is in $ctype_array & return the value. If not, send default.
$ctype = array_key_exists($file_extension, $ctype_array) ? $ctype_array[$file_extension] : 'application/force-download';
That way you can easily add more items to the $ctype_array without having to juggle switch/case logic.
<?php
if(isset($_POST['sportname'])){
include("connect.php");
$name = $_POST['sportname'];
$sql = "Select docs From tbl_sport WHERE sprt_name= '".$name."'";
$result = mysql_query($sql);
$row=mysql_fetch_object($result);
$file = $row->docs;
ini_set("allow-url-fopen", true);
// Fetch the file info.
$filePath = 'http://prisaa-region11.com/Docs/'.$file;
if(file_exists($filePath)) {
$fileName = basename($filePath);
$fileSize = filesize($filePath);
// Output headers.
header("Cache-Control: private");
header("Content-Type: application/stream");
header("Content-Length: ".$fileSize);
header("Content-Disposition: attachment; filename=".$fileDisplayName);
// Output file.
readfile ($filePath);
exit();
}
else {
die('The provided file path is not valid.');
}
}
?>
i dont know whats wrong with the path
You might want to try Filter Validator
if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
die('Not a valid URL');
}
Check this similar thread
Best way to check if a URL is valid
I coded a script that when users want to download a file, it shows an advert first and then start the download passing the ID of the file via $_GET.
Problem is that if I reach the page with no ID specified (download_file.php instead of download_file.php?id=1, for instance), the page starts the download of the page itself.
<?php
require("/membri/lostlife/mysql.php");
// Variables:
$id = $_GET["id"];
$result = mysql_query("SELECT * FROM Setting WHERE ID = $id");
$row = mysql_fetch_array($result);
$downloads = $row["Downloads"] + 1;
//
switch ($_GET["action"])
{
case "download":
// Download the file:
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=\"$row[Filename]\"");
readfile("/membri/lostlife/setting/$row[Filename]");
// Update the database:
mysql_query("UPDATE Setting SET Downloads = $downloads WHERE ID = $id");
break;
default:
echo "";
header("Refresh: 5; url=?id=$id&action=download");
}
?>
That's my code. What's wrong with it?
Also you got in your default from your switch a refresh header.. so when the action is NOT 'download' it is going to refresh to action=download.
ill would do it this way:
require("/membri/lostlife/mysql.php");
$id = $_GET["id"];
$action = $_GET["action"];
// if its not empty and it is numeric(check if its a integer can be done in different ways)
if(!empty($id) && is_numeric($id))
{
$query = mysql_query("SELECT Downloads, Filename FROM Setting WHERE ID = $id");
$row = mysql_fetch_assoc($query);
$download = $row['Downloads'];
$filename = $row[Filename];
if($action == "downoad") {
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=\"". $filename ."\"");
readfile("/membri/lostlife/setting/". $filename);
}
}
else
{
die("No ID found");
};
You also updating something? what your doing know is update the download what you got from your select statement? so you don't need to update it? you do you want to count what you download?