Digital download link with php - php

I use this function to make Digital download link . test.zip send my browser but file size unknown and when i download it and open show this error : "the archive is either is unknown format or damaged"
test.zip inside file folder and download.php Inside root.
<?php
/* Tutorial by AwesomePHP.com -> www.AwesomePHP.com */
/* Function: download with resume/speed/stream options */
/*
Parametrs: downloadFile(File Location, File Name,
max speed, is streaming
If streaming - movies will show as movies, images as images
instead of download prompt
*/
function downloadFile($fileLocation,$fileName,$maxSpeed = 13,$doStream =
false){
if (connection_status()!=0) return(false);
$array = explode('.',$fileName);
$extension = strtolower(end($array));
/* List of File Types */
$fileTypes['swf'] = 'application/x-shockwave-flash';
$fileTypes['pdf'] = 'application/pdf';
$fileTypes['exe'] = 'application/octet-stream';
$fileTypes['zip'] = 'application/zip';
$fileTypes['doc'] = 'application/msword';
$fileTypes['xls'] = 'application/vnd.ms-excel';
$fileTypes['ppt'] = 'application/vnd.ms-powerpoint';
$fileTypes['gif'] = 'image/gif';
$fileTypes['png'] = 'image/png';
$fileTypes['jpeg'] = 'image/jpg';
$fileTypes['jpg'] = 'image/jpg';
$fileTypes['rar'] = 'application/rar';
$fileTypes['ra'] = 'audio/x-pn-realaudio';
$fileTypes['ram'] = 'audio/x-pn-realaudio';
$fileTypes['ogg'] = 'audio/x-pn-realaudio';
$fileTypes['wav'] = 'video/x-msvideo';
$fileTypes['wmv'] = 'video/x-msvideo';
$fileTypes['avi'] = 'video/x-msvideo';
$fileTypes['asf'] = 'video/x-msvideo';
$fileTypes['divx'] = 'video/x-msvideo';
$fileTypes['mp3'] = 'audio/mpeg';
$fileTypes['mp4'] = 'audio/mpeg';
$fileTypes['mpeg'] = 'video/mpeg';
$fileTypes['mpg'] = 'video/mpeg';
$fileTypes['mpe'] = 'video/mpeg';
$fileTypes['mov'] = 'video/quicktime';
$fileTypes['swf'] = 'video/quicktime';
$fileTypes['3gp'] = 'video/quicktime';
$fileTypes['m4a'] = 'video/quicktime';
$fileTypes['aac'] = 'video/quicktime';
$fileTypes['m3u'] = 'video/quicktime';
$contentType = $fileTypes[$extension];
header("Cache-Control: public");
header("Content-Transfer-Encoding: binary\n");
header("Content-Type: $contentType");
$contentDisposition = 'attachment';
if($doStream == true){
/* extensions to stream */
$array_listen = array('mp3','m3u','m4a','mid','ogg','ra','ram','wm',
'wav','wma','aac','3gp','avi','mov','mp4','mpeg','mpg','swf','wmv','divx','asf');
if(in_array($extension,$array_listen)){
$contentDisposition = 'inline';
}
}
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
$fileName= preg_replace('/\./', '%2e', $fileName,
substr_count($fileName, '.') - 1);
header("Content-Disposition: $contentDisposition; filename=".$fileName);
} else {
header("Content-Disposition: $contentDisposition; filename=".$fileName);
}
header("Accept-Ranges: bytes");
$range = 0;
$size = filesize($fileLocation);
if(isset($_SERVER['HTTP_RANGE'])) {
list($a, $range)=explode("=",$_SERVER['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2=$size-1;
$new_length=$size-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
} else {
$size2=$size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size);
}
if ($size == 0 ) { die('Zero byte file! Aborting download');}
set_magic_quotes_runtime(0);
$fp=fopen("$fileLocation","rb");
fseek($fp,$range);
while(!feof($fp) and (connection_status()==0))
{
set_time_limit(0);
print(fread($fp,1024*$maxSpeed));
flush();
ob_flush();
sleep(1);
}
fclose($fp);
return((connection_status()==0) and !connection_aborted());
}
/* Implementation */
downloadFile('file/','test.zip',900,false);
?>

I read the function and I solve these issues:
1.
$fileLocation = $fileLocation . $fileName;
$fileLocation used to read file, but it's only contained folder name!
2.
$range = str_replace($range, "-", $range);
str_replace replaces "-" character, but didn't update $range
3.
set_magic_quotes_runtime is deprecated, add a check before calling that function!
You can download new code from my github ;)

Get rid of the ?> at the end of the file.
If that doesn't work post the zipfile somewhere where I can see it.

Related

Why does Mozilla Extension like downthemall cause requests to a URL to appear hitting multiple times?

I develop a PHP code and count how much data was downloaded by the client. My code is working fine if someone directly requests my file from a browser. But if someone installs a Mozilla extensions like ‘’downthemall’’ and then the url is requested by that extension then it requests the file URL 4 times so I get the downloaded data 4 times. How do I fix this? The same happened when the user is using jDownloader software or any software.
<?php
function getMimeType($fileName)
{
$c_type_default = "application/octet-stream";
$content_types = array(
"exe" => "application/octet-stream",
"zip" => "application/zip",
"mp3" => "audio/mpeg",
"mpg" => "video/mpeg",
"avi" => "video/x-msvideo",
"mp4" => "video/mp4",
);
// echo isset($content_types[pathinfo($fileName, PATHINFO_EXTENSION)]) ? $content_types[pathinfo($fileName, PATHINFO_EXTENSION)] : $c_type_default;
return isset($content_types[pathinfo($fileName, PATHINFO_EXTENSION)]) ? $content_types[pathinfo($fileName, PATHINFO_EXTENSION)] : $c_type_default;
}
function output_file($file, $name)
{
/*
This function takes a path to a file to output ($file), the filename that the browser will see ($name) and the MIME type of the file ($mime_type, optional).
*/
//Check the file premission
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
//turn off output buffering to decrease cpu usage
#ob_end_clean();
include('../includes/configg.php');
// Start Code For Total Downloaded Data Size by The LoggedIn User
$user="select * from $TBusers where id=".$_SESSION['log_id'];
$result_user = mysqli_query($conn, $user) ;
$row = mysqli_fetch_assoc($result_user);
$lastsotrage = $row['down'];
$curr_date= $row['curr_date'];
$today_date= date('d-m-y');
$daily_downloaded_data= $row['daily_downloaded_data'];
if($curr_date != $today_date)
{
$update_curr_date="update $TBusers set curr_date ='$today_date', daily_downloaded_data =0 where id=".$_SESSION['log_id']; // Set Curr_date by Today Date For check Daily Downloaded Data Limit
mysqli_query($conn,$update_curr_date) ;
$daily_downloaded_data=0;
}
/*else
{
if($daily_downloaded_data>=(20*1024*1024*1024)){ // Check Daily Download Data Limit 20GB Exceed or not
header('Location: http://idownload.club/members/download.php');
exit;}
}*/
// required for IE, otherwise Content-Disposition may be ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
header('Content-Type: ' . getMimeType($name));
header('Content-Disposition: attachment; filename="'.$name.'"');
header("Content-Transfer-Encoding: binary");
header('Accept-Ranges: bytes');
/* The three lines below basically make the
download non-cacheable */
header("Cache-control: private");
header('Pragma: private');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
// multipart-download and download resuming support
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
list($range) = explode(",",$range,2);
list($range, $range_end) = explode("-", $range);
$range=intval($range);
if(!$range_end) {
$range_end=$size-1;
} else {
$range_end=intval($range_end);
}
$new_length = $range_end-$range+1;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range-$range_end/$size");
} else {
$new_length=$size;
header("Content-Length: ".$size);
}
/* Will output the file itself */
$chunksize = 1*(1024*1024); //you may want to change this
$bytes_send = 0;
if ($file = fopen($file, 'r'))
{
if(isset($_SERVER['HTTP_RANGE']))
fseek($file, $range);
while(!feof($file) &&
(!connection_aborted()) &&
($bytes_send<$new_length)
)
{
$buffer = fread($file, $chunksize);
print($buffer); //echo($buffer); // can also possible
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
$lastsotrage= $lastsotrage + $size;
$daily_downloaded_data+=$size;
$sql="update $TBusers set down =". $lastsotrage." where id=".$_SESSION['log_id'];
$sql2="update $TBusers set daily_downloaded_data = $daily_downloaded_data where id=".$_SESSION['log_id'];
$sql3= "update test set count=count+1 where id=1" ;
mysqli_query($conn,$sql) ;
mysqli_query($conn,$sql2) ;mysqli_query($conn,$sql3) ;
file_put_contents('data.txt', print_r($_SERVER, true), FILE_APPEND);
} else
//If no permissiion
die('Error - can not open file.');
//die
die();
}
//Set the time out
set_time_limit(0);
//path to the file
$file_dir=$_GET['directory'];
$file_name = str_replace("/", "", $_GET['file']);
$file_name = str_replace("\\", "", $file_name);
$file = $file_dir . "/".$file_name;
//Call the download function with file path,file name and file type
output_file($file, ''.$file_name.'');
?>

PHP download script failed when download file > 1GB show network failed error

I am using the following code to download a file in PHP. Code works fine for most videos, but when I try to download video larger than 1 GB it failed and shows me a download error when it get to 1gb (exactly 1gb, no less, no more), this error happens with all browsers. But, if I use jdownloader or DownThemAll or any software all downloads are succesfully downloaded.
<?php
set_time_limit(0);
ini_set('memory_limit', '2G');
function getMimeType($fileName)
{
$c_type_default = "application/octet-stream";
$content_types = array(
"exe" => "application/octet-stream",
"zip" => "application/zip",
"mp3" => "audio/mpeg",
"mpg" => "video/mpeg",
"avi" => "video/x-msvideo",
"mp4" => "video/mp4",
);
// echo isset($content_types[pathinfo($fileName, PATHINFO_EXTENSION)]) ? $content_types[pathinfo($fileName, PATHINFO_EXTENSION)] : $c_type_default;
return isset($content_types[pathinfo($fileName, PATHINFO_EXTENSION)]) ? $content_types[pathinfo($fileName, PATHINFO_EXTENSION)] : $c_type_default;
}
function output_file($file, $name)
{
/*
This function takes a path to a file to output ($file), the filename that the browser will see ($name) and the MIME type of the file ($mime_type, optional).
*/
//Check the file premission
if(!is_readable($file)) die('File not found or inaccessible!');
$size = filesize($file);
$name = rawurldecode($name);
//turn off output buffering to decrease cpu usage
#ob_end_clean();
include('../includes/configg.php');
// Start Code For Total Downloaded Data Size by The LoggedIn User
$user="select * from $TBusers where id=".$_SESSION['log_id'];
$result_user = mysqli_query($conn, $user) ;
$row = mysqli_fetch_assoc($result_user);
$lastsotrage = $row['down'];
$curr_date= $row['curr_date'];
$today_date= date('d-m-y');
$daily_downloaded_data= $row['daily_downloaded_data'];
if($curr_date != $today_date)
{
$update_curr_date="update $TBusers set curr_date ='$today_date', daily_downloaded_data =0 where id=".$_SESSION['log_id']; // Set Curr_date by Today Date For check Daily Downloaded Data Limit
mysqli_query($conn,$update_curr_date) ;
$daily_downloaded_data=0;
}
/*else
{
if($daily_downloaded_data>=(20*1024*1024*1024)){ // Check Daily Download Data Limit 20GB Exceed or not
header('Location: http://idownload.club/members/download.php');
exit;}
}*/
// required for IE, otherwise Content-Disposition may be ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
header('Content-Type: ' . getMimeType($name));
header('Content-Disposition: attachment; filename="'.$name.'"');
header("Content-Transfer-Encoding: binary");
header('Accept-Ranges: bytes');
/* The three lines below basically make the
download non-cacheable */
header("Cache-control: private");
header('Pragma: private');
header("Expires: 0");
// multipart-download and download resuming support
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
list($range) = explode(",",$range,2);
list($range, $range_end) = explode("-", $range);
$range=intval($range);
if(!$range_end) {
$range_end=$size-1;
} else {
$range_end=intval($range_end);
}
$new_length = $range_end-$range+1;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range-$range_end/$size");
} else {
$new_length=$size;
header("Content-Length: ".$size);
}
/* Will output the file itself */
$chunksize = 8*(1024*1024); //you may want to change this
$bytes_send = 0;
if ($file = fopen($file, 'r'))
{
if(isset($_SERVER['HTTP_RANGE']))
fseek($file, $range);
while(!feof($file) &&
(!connection_aborted()) &&
($bytes_send<$new_length)
)
{
$buffer = fread($file, $chunksize);
print($buffer); //echo($buffer); // can also possible
//flush();
$bytes_send += strlen($buffer);
}
fclose($file);
$lastsotrage= $lastsotrage + $size;
$daily_downloaded_data+=$size;
$sql="update $TBusers set down =". $lastsotrage." where id=".$_SESSION['log_id'];
$sql2="update $TBusers set daily_downloaded_data = $daily_downloaded_data where id=".$_SESSION['log_id'];
$sql3= "update test set count=count+1 where id=1" ;
mysqli_query($conn,$sql) ;
mysqli_query($conn,$sql2) ;mysqli_query($conn,$sql3) ;
file_put_contents('data.txt', print_r($_SERVER, true), FILE_APPEND);
} else
//If no permissiion
die('Error - can not open file.');
//die
die();
}
//Set the time out
set_time_limit(0);
//path to the file
$file_dir=$_GET['directory'];
$file_name = str_replace("/", "", $_GET['file']);
$file_name = str_replace("\\", "", $file_name);
$file = $file_dir . "/".$file_name;
//Call the download function with file path,file name and file type
output_file($file, ''.$file_name.'');
?>

resume and section support in php

sorry if this is not a new question but i'm really stuck in this problem. I'm using script below for downloading a large file like a movie, I can not let user to access direct link of the file, also I need to let user download the file in a resume and section support manner. Using this code I just have resume support, not section. I'm using Yii framework.
Please Help me on this by any solution and suggestion.
public static function downloadFile($fileLocation, $saveName = null, $maxSpeed = 100, $doStream = false){
$start = 0;
$end = -1;
$section = false;
$extension = CFileHelper::getExtension($fileLocation);
$fileName = is_null($saveName) ? basename($fileLocation) : $saveName . '.' . $extension;
/* #var $contentType string mime type for the file, if is null, it will be octet-stream */
$contentType = CFileHelper::getMimeType($fileLocation);
$contentType = is_null($contentType) ? 'application/octet-stream' : $contentType;
if(isset($_SERVER['HTTP_RANGE']))
{
$range2 = substr($_SERVER['HTTP_RANGE'], strlen('bytes='));
$range = explode('-', $range2);
if($range[0] > 0)
$start = intval($range[0]);
if($range[1] > 0)
$end = intval($range[1]);
$section = true;
}
ob_end_clean();
$old_status = ignore_user_abort(true);
set_time_limit(0);
$size = filesize($fileLocation);
if($start > ($size -1)) $start = 0;
$fp = fopen($fileLocation, 'rb');
if($start) fseek($fp, $start);
if($end < $start) $end = $size -1;
header('Content-Type: '.$contentType);
$contentDisposition = 'attachment';
if($doStream == true){
$array_listen = array('mp3','m3u','m4a','mid','ogg','ra','ram','wm',
'wav','wma','aac','3gp','avi','mov','mp4','mpeg','mpg','swf','wmv','divx','asf');
if(in_array($extension,$array_listen)){
$contentDisposition = 'inline';
}
}
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
$fileName= preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
header("Content-Disposition: $contentDisposition; filename=\"$fileName\"");
} else {
header("Content-Disposition: $contentDisposition; filename=\"$fileName\"");
}
header('Content-Disposition: ' . $contentDisposition . '; filename="' . $fileName . '"');
header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T', filemtime($fileLocation)));
if($section)
{
header("HTTP/1.0 206 Partial Content");
header("Status: 206 Partial Content");
header('Accept-Ranges: bytes');
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: " . ($end - $start + 1));
}else
header('Content-Length: '.$size);
$size = $end - $start + 1;
while(!(connection_aborted() || connection_status() == 1) && !feof($fp))
{
print(fread($fp,1024*$maxSpeed));
flush();
ob_flush();
sleep(1);
}
fclose($fp);
ignore_user_abort($old_status);
set_time_limit(ini_get('max_execution_time'));
}
If you are using apache and able to install mods to it, you can use mod_xsendfile module. It supports partial downloads, cashing and other cool things.
Simple demo: Send Files Faster & Better with PHP & X-Sendfile

Download with resume

I've searched many topics and posts and made my own downloading snippet with 2 functions as below!
Download starts with no problem, custom errors are shown when user hasn't enough credit, all things are right but the resume! I thought it maybe Headers, but I didn't miss them.
I read many posts on stackoverflow too.
Helps needed!
function readfile_chunked($filename, $size = '', $uid, $pid, $retbytes = TRUE)
{
global $each_download;
$chunk_size = 1024*1024;
$buffer = '';
$cnt =0;
$handle = fopen($filename, 'rb');
if ($handle === false)
{
return false;
}
//check if http_range is sent by browser (or download manager)
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range)=explode("=",$_SERVER['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2=$size-1;
$new_length=$size2-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
fseek($fp,$range);
}
else
{
if(get_user_meta($uid, 'revo_dropped', true) != $pid)
{
update_user_meta($uid, 'dropped', $pid);
update_user_meta($uid, 'credits', get_user_meta($uid, 'credits', true)-$each_download);
}
$size2 = $size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size);
}
while (!feof($handle) && connection_status()==0)
{
#set_time_limit(0);
$buffer = fread($handle, $chunk_size);
echo $buffer;
ob_flush();
flush();
if ($retbytes)
{
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status)
{
return $cnt;
}
return $status;
}
function save_file()
{
global $wpdb;
global $uid;
global $each_download;
$hash = #$_GET["download"];
ini_set('zlib.output_compression', 'Off');
set_time_limit(0);
session_cache_limiter('none');
set_magic_quotes_runtime(0);
if(preg_match('/^[a-z0-9]{32}$/i',$hash))
{
if($row = $wpdb->get_row("SELECT * FROM wp_dlurl WHERE hash = '{$hash}'",ARRAY_A))
{
if(is_user_logged_in())
{
if($row['price'] != 0)
$each_download = $row['price'];
if(get_user_meta($uid, 'credits', true) >= $each_download)
{
$parts = pathinfo($row['url']);
$url = $parts['dirname'] . '/' . $parts['basename'];//urlencode($parts['basename']);
$file = pathinfo($row['filename']);
$ext = $file['extension'];
/* List of File Types */
$fileTypes['swf'] = 'application/x-shockwave-flash';
$fileTypes['pdf'] = 'application/pdf';
$fileTypes['exe'] = 'application/octet-stream';
$fileTypes['zip'] = 'application/zip';
$fileTypes['doc'] = 'application/msword';
$fileTypes['xls'] = 'application/vnd.ms-excel';
$fileTypes['ppt'] = 'application/vnd.ms-powerpoint';
$fileTypes['gif'] = 'image/gif';
$fileTypes['png'] = 'image/png';
$fileTypes['jpeg'] = 'image/jpg';
$fileTypes['jpg'] = 'image/jpg';
$fileTypes['rar'] = 'application/rar';
$fileTypes['ra'] = 'audio/x-pn-realaudio';
$fileTypes['ram'] = 'audio/x-pn-realaudio';
$fileTypes['ogg'] = 'audio/x-pn-realaudio';
$fileTypes['wav'] = 'video/x-msvideo';
$fileTypes['wmv'] = 'video/x-msvideo';
$fileTypes['avi'] = 'video/x-msvideo';
$fileTypes['asf'] = 'video/x-msvideo';
$fileTypes['divx'] = 'video/x-msvideo';
$fileTypes['mp3'] = 'audio/mpeg';
$fileTypes['mp4'] = 'audio/mpeg';
$fileTypes['mpeg'] = 'video/mpeg';
$fileTypes['mpg'] = 'video/mpeg';
$fileTypes['mpe'] = 'video/mpeg';
$fileTypes['mov'] = 'video/quicktime';
$fileTypes['swf'] = 'video/quicktime';
$fileTypes['3gp'] = 'video/quicktime';
$fileTypes['m4a'] = 'video/quicktime';
$fileTypes['aac'] = 'video/quicktime';
$fileTypes['m3u'] = 'video/quicktime';
$contentType = $fileTypes[$ext];
//ob_end_clean();
ob_start();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Robots: none");
header("Content-Type: ".$contentType."");
header("Content-Description: File Transfer");
$new_name = $row['filename'];//rand(1000,999999).".".$ext;
$contentDisposition = 'attachment';
if(strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
$new_name = preg_replace('/\./', '%2e', $new_name, substr_count($new_name,'.') - 1);
}
$new_name = urlencode($new_name);
//#ob_end_clean();
header("Content-Disposition: ".$contentDisposition."; filename=\"".$new_name."\";");
header("Content-Transfer-Encoding: binary");
header("Accept-Ranges: bytes");
$size = $row['size'];
$thefile = $url;
$urlparsed = parse_url($thefile); // urlllllllllllll
$isURI = array_key_exists('scheme', $urlparsed);
$localURI = (bool) strstr($thefile, get_bloginfo('wpurl'));
$patterns = array( '|^'. get_bloginfo('wpurl') . '/' . '|');
$path = preg_replace( $patterns, '', $thefile );
// this is joining the ABSPATH constant, changing any slashes to local filesystem slashes, and then finally getting the real path.
$thefile = str_replace( '/', DIRECTORY_SEPARATOR, path_join( ABSPATH, $path ) );
///////////////////////////////////
if($size == 0)
{
showMessage('Size 0');
}
if (isset($size) && $size > 0)
{
//header("Content-Length: ".$size);
#readfile_chunked($thefile, $size, $uid, $pid);
}
///////////////////////////////////
exit;
}
else
showMessage("buy credit");
}
else
showMessage("login");
}
else
{
wp_redirect(get_bloginfo('url'));
exit;
}
}
else
{
wp_redirect(get_bloginfo('url'));
exit;
}
}
This script might help you out:
<?php
/**
* Copyright 2012 Armand Niculescu - media-division.com
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// get the file request, throw error if nothing supplied
// hide notices
#ini_set('error_reporting', E_ALL & ~ E_NOTICE);
//- turn off compression on the server
#apache_setenv('no-gzip', 1);
#ini_set('zlib.output_compression', 'Off');
if(!isset($_REQUEST['file']) || empty($_REQUEST['file']))
{
header("HTTP/1.0 400 Bad Request");
exit;
}
// sanitize the file request, keep just the name and extension
// also, replaces the file location with a preset one ('./myfiles/' in this example)
$file_path = $_REQUEST['file'];
$path_parts = pathinfo($file_path);
$file_name = $path_parts['basename'];
$file_ext = $path_parts['extension'];
$file_path = './myfiles/' . $file_name;
// allow a file to be streamed instead of sent as an attachment
$is_attachment = isset($_REQUEST['stream']) ? false : true;
// make sure the file exists
if (is_file($file_path))
{
$file_size = filesize($file_path);
$file = #fopen($file_path,"rb");
if ($file)
{
// set the headers, prevent caching
header("Pragma: public");
header("Expires: -1");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
header("Content-Disposition: attachment; filename=\"$file_name\"");
// set appropriate headers for attachment or streamed file
if ($is_attachment)
header("Content-Disposition: attachment; filename=\"$file_name\"");
else
header('Content-Disposition: inline;');
// set the mime type based on extension, add yours if needed.
$ctype_default = "application/octet-stream";
$content_types = array(
"exe" =--> "application/octet-stream",
"zip" => "application/zip",
"mp3" => "audio/mpeg",
"mpg" => "video/mpeg",
"avi" => "video/x-msvideo",
);
$ctype = isset($content_types[$file_ext]) ? $content_types[$file_ext] : $ctype_default;
header("Content-Type: " . $ctype);
//check if http_range is sent by browser (or download manager)
if(isset($_SERVER['HTTP_RANGE']))
{
list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2);
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
list($range, $extra_ranges) = explode(',', $range_orig, 2);
}
else
{
$range = '';
header('HTTP/1.1 416 Requested Range Not Satisfiable');
exit;
}
}
else
{
$range = '';
}
//figure out download piece from range (if set)
list($seek_start, $seek_end) = explode('-', $range, 2);
//set start and end based on range (if set), else set defaults
//also check for invalid ranges.
$seek_end = (empty($seek_end)) ? ($file_size - 1) : min(abs(intval($seek_end)),($file_size - 1));
$seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seek_start > 0 || $seek_end < ($file_size - 1))
{
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$file_size);
header('Content-Length: '.($seek_end - $seek_start + 1));
}
else
header("Content-Length: $file_size");
header('Accept-Ranges: bytes');
set_time_limit(0);
fseek($file, $seek_start);
while(!feof($file))
{
print(#fread($file, 1024*8));
ob_flush();
flush();
if (connection_status()!=0)
{
#fclose($file);
exit;
}
}
// file save was a success
#fclose($file);
exit;
}
else
{
// file couldn't be opened
header("HTTP/1.0 500 Internal Server Error");
exit;
}
}
else
{
// file does not exist
header("HTTP/1.0 404 Not Found");
exit;
}
Taken from:
http://www.richnetapps.com/php-download-script-with-resume-option/

Download resume and zip file issue - PHP [duplicate]

This question already has answers here:
PHP Remote file streaming with Resume Support
(3 answers)
Closed 9 years ago.
I'm really stuck with it. I made a function as below (a wp plugin). That handles download requests.
The download url format is like this:
http://mysite.com/?download=2f547re8w9qasd547g8tr52e15469879w
Files are going to be saved as usual (with that pop up "save as..." box) in browser.
The problem is that:
1 - a download manager like Orbit tries to download a page! and IDM just downloads a low percentage of whole file size (e.g. 7%) then stops & by resuming, it begins downloading from first. (browser downloads files completely and resumes without problem)
2 - when a .zip file is downloaded (by browser), has an "Unexpected end of archive" error. (CRC32 in zip file details shows 0 chars)
function save_file()
{
global $wpdb;
global $uid;
global $each_download;
$hash = #$_GET["download"];
if(preg_match('/^[a-z0-9]{32}$/i',$hash))
{
if($row = $wpdb->get_row("SELECT * FROM wp_dlurl WHERE hash = '{$hash}'",ARRAY_A))
{
if(is_user_logged_in())
{
if($row['price'] != 0)
$each_download = $row['price'];
if(get_user_meta($uid, 'revo_credits', true) >= $each_download)
{
$parts = pathinfo($row['url']);
$url = $parts['dirname'] . '/' . urlencode($parts['basename']);
$file = pathinfo($row['filename']);
$ext = $file['extension'];
/* List of File Types */
$fileTypes['swf'] = 'application/x-shockwave-flash';
$fileTypes['pdf'] = 'application/pdf';
$fileTypes['exe'] = 'application/octet-stream';
$fileTypes['zip'] = 'application/zip';
$fileTypes['doc'] = 'application/msword';
$fileTypes['xls'] = 'application/vnd.ms-excel';
$fileTypes['ppt'] = 'application/vnd.ms-powerpoint';
$fileTypes['gif'] = 'image/gif';
$fileTypes['png'] = 'image/png';
$fileTypes['jpeg'] = 'image/jpg';
$fileTypes['jpg'] = 'image/jpg';
$fileTypes['rar'] = 'application/rar';
$fileTypes['ra'] = 'audio/x-pn-realaudio';
$fileTypes['ram'] = 'audio/x-pn-realaudio';
$fileTypes['ogg'] = 'audio/x-pn-realaudio';
$fileTypes['wav'] = 'video/x-msvideo';
$fileTypes['wmv'] = 'video/x-msvideo';
$fileTypes['avi'] = 'video/x-msvideo';
$fileTypes['asf'] = 'video/x-msvideo';
$fileTypes['divx'] = 'video/x-msvideo';
$fileTypes['mp3'] = 'audio/mpeg';
$fileTypes['mp4'] = 'audio/mpeg';
$fileTypes['mpeg'] = 'video/mpeg';
$fileTypes['mpg'] = 'video/mpeg';
$fileTypes['mpe'] = 'video/mpeg';
$fileTypes['mov'] = 'video/quicktime';
$fileTypes['swf'] = 'video/quicktime';
$fileTypes['3gp'] = 'video/quicktime';
$fileTypes['m4a'] = 'video/quicktime';
$fileTypes['aac'] = 'video/quicktime';
$fileTypes['m3u'] = 'video/quicktime';
$contentType = $fileTypes[$ext];
//ob_end_clean();
header("Cache-Control: public");
header('Content-Type: $contentType');
header("Content-Transfer-Encoding: binary");
$new_name = $row['filename'];//rand(1000,999999).".".$ext;
$contentDisposition = 'attachment';
if(strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
$new_name = preg_replace('/\./', '%2e', $new_name, substr_count($new_name,'.') - 1);
}
$new_name = urlencode($new_name);
header("Content-Disposition: $contentDisposition; filename=\"$new_name\"");
header("Accept-Ranges: bytes");
$range = 0;
$size = $row['size'];
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2 = $size-1;
$new_length = $size-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
}
else
{
update_user_meta($uid, 'revo_credits', get_user_meta($uid, 'revo_credits', true)-$each_download);
$size2 = $size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size);
}
if ($size == 0)
{
showMessage('aborted. zero file size');
}
set_magic_quotes_runtime(0);
$maxSpeed = 200;
$fp = fopen($url,"rb");
fseek($fp,$range);
while(!feof($fp) and (connection_status()==0))
{
set_time_limit(0);
print(fread($fp,1024*$maxSpeed));
flush();
ob_flush();
sleep(1);
}
fclose($fp);
return((connection_status()==0) and !connection_aborted());
}
else
showMessage("not enough credit.");
}
else
showMessage("login to download.");
}
else
{
wp_redirect(get_bloginfo('url'));
exit;
}
}
else
{
wp_redirect(get_bloginfo('url'));
exit;
}
}
I think headers are the culprits!
Resuming method with Accept-ranges and Content-length headers are already done and works when file is downloading by browser.
Am not sure how other download works but your range implementation is wrong why ?
You are getting range like this
$_SERVER['HTTP_RANGE'] = "bytes=1-200"; //sample range
$size = 1000; //sample size
list($a, $range) = explode("=", $_SERVER['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2 = $size - 1;
$new_length = $size - $range;
// Used Print Insted of headers
print("HTTP/1.1 206 Partial Content\n");
print("Content-Length: $new_length\n");
print("Content-Range: bytes $range$size2/$size\n");
Output
HTTP/1.1 206 Partial Content
Content-Length: 999
Content-Range: bytes 1-200999/1000 <------ This is an issue
I expect something like this
list($a, $range) = explode("=", $_SERVER['HTTP_RANGE']);
list($offset, $length) = explode("-", $range);
$length = $length - $offset;
// Used Print Insted of headers
print("HTTP/1.1 206 Partial Content\n");
print("Content-Length: $length\n");
printf('Content-Range: bytes %d-%d/%d', $offset, ($offset + $length), $size)
Output
HTTP/1.1 206 Partial Content
Content-Length: 199
Content-Range: bytes 1-200/1000 <---- Properly Displayed

Categories