Large files failing to download using readfile() - php

I have the following code to force download an IPA file (after codesigning it with a script). It works fine with smaller files but with larger files, my web server starts returning a 500 Internal Server Error. Would someone be able to help me tweak my existing code to overcome this issue?
$time = md5(time());
// Runs code signing script here
// And then attempts to initiate download
$path = "done/$time/";
$latest_ctime = 0;
$latest_filename = '';
$d = dir($path);
while (false !== ($entry = $d->read())) {
$filepath = "{$path}/{$entry}";
if (is_file($filepath) && filectime($filepath) > $latest_ctime) {
$latest_ctime = filectime($filepath);
$latest_filename = $entry;
}
}
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=$time.ipa");
header("Content-Type: application/ipa");
header("Content-Transfer-Encoding: binary");
// Read the file from disk
readfile("done/$time/".$latest_filename);
// header('location: dashboard.php');
} else {
// Throwback
die("Failed. Contact support. // <p>$sign</p>");
}

Here is an example:
$filepath = "done/{$time}/{$latest_filename}";
$size = filesize($filepath);
$mimetype = 'application/ipa';
// Turn off buffering
if (ob_get_level()) {
ob_end_clean();
}
$handle = fopen($filepath, 'rb');
if ($handle !== false && $size > 0) {
#flock($handle, LOCK_SH);
$filename = rawurldecode($filepath);
$old_max_execution_time = ini_get('max_execution_time');
$old_cache_limiter = session_cache_limiter();
ini_set('max_execution_time', 0);
session_cache_limiter(false);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header('Content-Type: ' . $mimetype);
header('Content-Transfer-Encoding: binary');
header('Content-disposition: attachment; filename="'. $filename .'"');
// or your variant
// header("Content-Disposition: attachment; filename=" . md5(time()));
header("Content-Length: $size");
$start = 0;
$end = $size - 1;
$chunk = 8 * 1024;
$requested = (float)$end - (float)$start + 1;
while (! $error) {
if ($chunk >= $requested) {
$chunk = (integer)$requested;
}
set_time_limit(0);
while (! feof($handle) && (connection_status() === 0)) {
if (! $buffer = #fread($handle, $chunk)) {
$error = true;
break 2;
}
print($buffer);
flush();
}
#flock($handle, LOCK_UN);
#fclose($handle);
ini_set('max_execution_time', $old_max_execution_time);
session_cache_limiter($old_cache_limiter);
break;
}
if ($error) {
// 500 - Internal server error
exit;
}
} else {
// Can't open file
exit;
}

Maybe problem in script time execution.
Try to set ini_set('max_execution_time', 0);
Also try to read and send file by chunks.

Related

PHP text file auto download giving error headers already sent

I am uploading text file and then converting it after conversion I am forcing it to auto download that converted text file, but it's not getting downloaded, giving error Warning: Cannot modify header information - headers already sent
Force download text file PHP code
if (file_exists($fileCreate)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($fileCreate));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($fileCreate));
ob_clean();
flush();
readfile($fileCreate);
exit;
}
}
Full PHP code
<?php
error_reporting(E_ALL & ~E_NOTICE);
if(!empty($_FILES['uploaded_file']))
{
$path = "upload/";
$path = $path . basename( $_FILES['uploaded_file']['name']);
if(move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $path)) {
echo "The file ". basename( $_FILES['uploaded_file']['name']).
" has been uploaded";
} else{
echo "There was an error uploading the file, please try again!";
}
$count = 0;
$fileCreate = 'order-'.date('m-d-Y').".txt";
$myfile = fopen("$fileCreate","w") or die("Unable to open file!");
fwrite($myfile, "1"."\n");
if($file = fopen("$path", "r")){
while(!feof($file)) {
$line = fgets($file);
$keywords = preg_split("/[\s,]+/", $line);
$x = $keywords[2];
$y = 5;
$y .= $x;
$y .= "001";
$date = $keywords[1];
$date1 = str_replace('"','',$date);
$newDate = date("Y/m/d", strtotime($date1));
$y .= str_replace("/", "", $newDate);
$y .= " ";
$fso = $keywords[3];
$fso = str_replace('"','',$fso);
$y .= $keywords[3];
if($fso != "FPO" || $fso != "APO"){
$y .= $keywords[4];
}
$y = str_replace('"','',$y);
$storeValue[$count]=$y;
fwrite($myfile, $y."\n");
$count++;
}
$str = strval($count);
$strlen = strlen($str);
if($strlen == 1){
$footer = 900000;
}
else if($strlen == 2){
$footer = 90000;
}
else if($strlen == 3){
$footer = 9000;
}
else if($strlen == 4){
$footer = 900;
}
else if($strlen == 5){
$footer = 90;
}
else{
$footer = 9;
}
$footer .= $count;
$footerDate = date("Y/m/d");
$footer .= str_replace("/", "", $footerDate);
fwrite($myfile, $footer."\n");
fclose($file);
}
if (file_exists($fileCreate)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($fileCreate));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($fileCreate));
ob_clean();
flush();
readfile($fileCreate);
exit;
}
}
?>
UPDATE: It's fixed by adding output_buffering = ON in php.ini file
Two things.
1) You need to start session immediately after the <?php
2) You session redirect does not work if something output before the session redirect (header location).
What you can do is,
You can use javascript for redirecting.
echo '<script>window.location.replace("http://stackoverflow.com");</script>';
Anyway if you are using sessions, start session on the top of page (session_start()).
<?php
session_start();
error_reporting(E_ALL & ~E_NOTICE);
if(!empty($_FILES['uploaded_file']))
Update
It's fixed by adding output_buffering = ON in php.ini file

Unable to view the download PDF file in browser in php

I have a download media function in which all type of media files gets downloaded. Below is the code
public function downloadMedia($file, $filename_direct = '', $extern = '', $exitHere = 1)
{
jimport('joomla.filesystem.file');
clearstatcache();
if (!$extern)
{
if (!JFile::exists($file))
{
return 2;
}
else
{
$len = filesize($file);
}
}
else
{
/* Return the size of a remote url or a local file specified by $url.
$thereturn specifies the unit returned (either bytes "", MiB "mb" or KiB
"kb"). */
$len = filesize($file);
}
$filename = basename($file);
$file_extension = strtolower(substr(strrchr($filename, "."), 1));
$ctype = $this->getMime($file_extension);
ob_end_clean();
// Needed for MS IE - otherwise content disposition is not used?
if (ini_get('zlib.output_compression'))
{
ini_set('zlib.output_compression', 'Off');
}
header("Cache-Control: public, must-revalidate");
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Expires: 0");
header("Content-Description: File Transfer");
header("Content-Type: " . $ctype);
header("Content-Length: " . (string) $len);
header('Content-Disposition: attachment; filename="' . $filename . '"');
// set_time_limit doesn't work in safe mode
if (!ini_get('safe_mode'))
{
#set_time_limit(0);
}
/*#readfile($file);
if ($exitHere == 1)
{
exit;
}*/
$fp = fopen($file, "r") ;
ob_clean();
flush();
while (!#feof($fp)) {
$buff = #fread($fp, $len);
print $buff;
}
exit;
}
Now the issue is, whenever I download the PDF file & clicked on downloaded file it shows an error in a browser like 'Failed to load PDF document.'
The downloaded PDF file is opened correctly only when if I open the file in adobe instead of opening it in browser.

How to download large file from a link by php?

I am trying to download a large file from an external link using php, I don't have a big idea about this subject, I tried to use the code below, but it always giving me file does not exist!.
I changed the if(file_exists($filePath)) to if(true), but I end with a downloaded file of 0 byte size, where is the error in my code?
$filePath = "http://down.egyu.net/Movies/The.Gambler.2014.720p.BluRay.x264.EGFire.CoM.mp4"; // set your download file path here.
download($filePath); // calls download function
function download($filePath)
{
if(!empty($filePath))
{
$fileInfo = pathinfo($filePath);
$fileName = $fileInfo['basename'];
$fileExtnesion = $fileInfo['extension'];
$default_contentType = "application/octet-stream";
$content_types_list = mimeTypes();
// to find and use specific content type, check out this IANA page : http://www.iana.org/assignments/media-types/media-types.xhtml
if (array_key_exists($fileExtnesion, $content_types_list))
{
$contentType = $content_types_list[$fileExtnesion];
}
else
{
$contentType = $default_contentType;
}
if(file_exists($filePath))
{
$size = filesize($filePath);
$offset = 0;
$length = $size;
//HEADERS FOR PARTIAL DOWNLOAD FACILITY BEGINS
if(isset($_SERVER['HTTP_RANGE']))
{
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = intval($matches[2]) - $offset;
$fhandle = fopen($filePath, 'r');
fseek($fhandle, $offset); // seek to the requested offset, this is 0 if it's not a partial content request
$data = fread($fhandle, $length);
fclose($fhandle);
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $size);
}//HEADERS FOR PARTIAL DOWNLOAD FACILITY BEGINS
//USUAL HEADERS FOR DOWNLOAD
header("Content-Disposition: attachment;filename=".$fileName);
header('Content-Type: '.$contentType);
header("Accept-Ranges: bytes");
header("Pragma: public");
header("Expires: -1");
header("Cache-Control: no-cache");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
header("Content-Length: ".filesize($filePath));
$chunksize = 8 * (1024 * 1024); //8MB (highest possible fread length)
if ($size > $chunksize)
{
$handle = fopen($_FILES["file"]["tmp_name"], 'rb');
$buffer = '';
while (!feof($handle) && (connection_status() === CONNECTION_NORMAL))
{
$buffer = fread($handle, $chunksize);
print $buffer;
ob_flush();
flush();
}
if(connection_status() !== CONNECTION_NORMAL)
{
echo "Connection aborted";
}
fclose($handle);
}
else
{
ob_clean();
flush();
readfile($filePath);
}
}
else
{
echo 'File does not exist!';
}
}
else
{
echo 'There is no file to download!';
}
}
I found the answer myself, I used a function to get the status of url if it exist and to get the file size:
/* You may need these ini settings too */
set_time_limit(0);
ini_set('memory_limit', '512M');
//THE DOWNLOAD SCRIPT
$filePath = "http://down.egyu.net/Movies/The.Gambler.2014.720p.BluRay.x264.EGFire.CoM.mp4"; // set your download file path here.
download($filePath); // calls download function
function download($filePath)
{
if(!empty($filePath))
{
$fileInfo = pathinfo($filePath);
$fileName = $fileInfo['basename'];
$fileExtnesion = $fileInfo['extension'];
$default_contentType = "application/octet-stream";
$content_types_list = mimeTypes();
// to find and use specific content type, check out this IANA page : http://www.iana.org/assignments/media-types/media-types.xhtml
if (array_key_exists($fileExtnesion, $content_types_list))
{
$contentType = $content_types_list[$fileExtnesion];
}
else
{
$contentType = $default_contentType;
}
$exist = is_url_exist($fileInfo['dirname']."/".$fileInfo['basename']);
if($exist['response'])
{
$size = $exist['size'];
$offset = 0;
$length = $size;
//HEADERS FOR PARTIAL DOWNLOAD FACILITY BEGINS
if(isset($_SERVER['HTTP_RANGE']))
{
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = intval($matches[2]) - $offset;
$fhandle = fopen($filePath, 'r');
fseek($fhandle, $offset); // seek to the requested offset, this is 0 if it's not a partial content request
$data = fread($fhandle, $length);
fclose($fhandle);
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $size);
}//HEADERS FOR PARTIAL DOWNLOAD FACILITY BEGINS
//USUAL HEADERS FOR DOWNLOAD
header("Content-Disposition: attachment;filename=".$fileName);
header('Content-Type: '.$contentType);
header("Accept-Ranges: bytes");
header("Pragma: public");
header("Expires: -1");
header("Cache-Control: no-cache");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
header("Content-Length: ".$size);
$chunksize = 8 * (1024 * 1024); //8MB (highest possible fread length)
if ($size > $chunksize)
{
$handle = fopen($_FILES["file"]["tmp_name"], 'rb');
$buffer = '';
while (!feof($handle) && (connection_status() === CONNECTION_NORMAL))
{
$buffer = fread($handle, $chunksize);
print $buffer;
ob_flush();
flush();
}
if(connection_status() !== CONNECTION_NORMAL)
{
echo "Connection aborted";
}
fclose($handle);
}
else
{
ob_clean();
flush();
readfile($filePath);
}
}
else
{
echo 'File does not exist!';
}
}
else
{
echo 'There is no file to download!';
}
}
function is_url_exist($url){
$array = array();
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
if($code == 200){
$array['response'] = true;
}else{
$array['response'] = false;
}
$array['size'] = $size;
curl_close($ch);
return $array;
}

Download of large files does not work

Hey i have following code to download a large file but the download does stop everytime without finish the download
function download($file)
{
include('logger.php5');
$log = new Logging();
$log->lfile('download.log');
ini_set('max_execution_time', 86400);
//header('Location: '.$file);
$filesize = filesize($file);
$filename = pathinfo($file, PATHINFO_BASENAME);
$filext = pathinfo($file, PATHINFO_EXTENSION);
$mime = include('mime.php5');
$log->lwrite(ini_get('max_execution_time'));
$log->lwrite(sprintf('%s %s %s %s', $filename, $filext, $mime[$filext], human_filesize($filesize)));
$log->lclose();
#ob_end_clean();
session_write_close();
header("Content-Description: File Transfer");
header("Content-Type: ".$mime[$filext]);
header("Content-Disposition: ".
(!strpos($HTTP_USER_AGENT,"MSIE 5.5")?"attachment; ":"").
"filename=".$filename);
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$filesize);
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header('Pragma: public');
header('Expires: 0');
$done = readfile_chunked($file);
}
function readfile_chunked($filename,$retbytes=true) {
$chunksize = 1*(1024*1024); // how many bytes per chunk
$buffer = '';
$cnt =0;
// $handle = fopen($filename, 'rb');
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
if ($retbytes) {
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // return num. bytes delivered like readfile() does.
}
return $status;
}
Each time i call the script the download start up but stops after 400MB, the file itself is 778MB big.
Someone can see a problem with the code?
UPDATE
after try to log the return value of readfile_chunkedit feels like the script gets stoped not the download itself. Because i cant get a log entry after the readfile_chunked call.
It could be a problem with the filesize function in PHP. There are known bugs for big file size reading and as you're sending it with the file as an header I would suggest you to try the script without using this line:
header("Content-Length: ".$filesize);
Oh and maybe you can take a look at this line:
header("Content-Transfer-Encoding: binary");
I think the encoding should be checked for each file. Like this:
$finfo = finfo_open(FILEINFO_MIME);
//check to see if the mime-type starts with 'text'
return substr(finfo_file($finfo, $filename), 0, 4) == 'text';
If it's a textfile you should use ASCII ofcourse. Has nothing to do with the question but I think it's an useful addition to your script :)

There's some html content in a file which is downloaded using PHP scripts on firefox

I use the following to download a file with PHP:
ob_start();
$browser = id_browser();
header('Content-Type: '.(($browser=='IE' || $browser=='OPERA')?
'application/octetstream':'application/octet-stream'));
header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize(realpath($fullpath)));
//header("Content-Encoding: none");
if($browser == 'IE')
{
header('Content-Disposition: attachment; filename="'.$file.'"');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
} else
{
header('Content-Disposition: attachment; filename="'.$file.'"');
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
}
//#set_time_limit( 0 );
ReadFileChunked(utf8_decode($fullpath));
ob_end_flush();
The source code of ReadFileChunked is:
function ReadFileChunked($filename,$retbytes=true)
{
$chunksize = 1*(1024*1024);
$remainFileSize = filesize($filename);
if($remainFileSize < $chunksize)
$chunksize = $remainFileSize;
$buffer = '';
$cnt =0;
// $handle = fopen($filename, 'rb');
//echo $filename."<br>";
$handle = fopen($filename, 'rb');
if ($handle === false) {
//echo 1;
return false;
}
//echo 2;
while (!feof($handle))
{
//echo "current remain file size $remainFileSize<br>";
//echo "current chunksize $chunksize<br>";
$buffer = fread($handle, $chunksize);
echo $buffer;
sleep(1);
ob_flush();
flush();
if ($retbytes) {
$cnt += strlen($buffer);
}
$remainFileSize -= $chunksize;
if($remainFileSize == 0)
break;
if($remainFileSize < $chunksize)
{
$chunksize = $remainFileSize;
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // return num. bytes delivered like readfile() does.
}
return $status;
}
The question is :
The file downloaded will contiain some html tags which are the content of the html code generated by the php.
The error will happened when downloading the txt file with the file size smaller than 4096 bytes.
Please help me to slove this problem , thank you very much!
Chu
Have you tried using fpassthru rather than your custom function.
There's no need to use the $chunksize stuff in there. fread() automatically stops reading once it reaches the end of the file, even if the $chunksize would normally tell it to read more. As well, you should probably put your ob_flush() and flush() calls BEFORE the sleep(1). That way the data you've just placed in the output buffer can get sent off to the webserver without having to wait the one second needlessly.
In fact, you could replace the whole function with the following:
function ReadFileChunk($filename, $retbytes = true) {
$fh = fopen($filename, 'rb');
if (!$fh) {
return(false);
}
while($buf = fread($fh, 4096)) {
echo $buf;
ob_flush();
flush();
sleep(1);
}
$status = fclose($fh);
return( $retbytes ? filesize($filename) : $status);
}
But why bother rolling your own when readfile() already exists? It will handle the whole business of opening the file, and sending it in normal-sized pieces that won't exceed memory_limit.

Categories