百科狗-知识改变命运!
--

readfile() - 输出文件 - php 文件目录函数

是丫丫呀1年前 (2023-11-21)阅读数 52#技术干货
文章标签文件

readfile()

(PHP 4, PHP 5, PHP 7)

输出文件

说明

readfile(string $filename[,bool $use_include_path= false[,resource $context]]): int

读取文件并写入到输出缓冲。

参数

$filename

要读取的文件名。

$use_include_path

readfile() - 输出文件 - php 文件目录函数

想要在include_path中搜索文件,可使用这个可选的第二个参数,设为TRUE

$context

Stream 上下文(context)resource。

返回值

返回从文件中读入的字节数。如果出错返回FALSE并且除非是以@readfile()形式调用,否则会显示错误信息。

范例

使用readfile()强制下载

Always using MIME-Type 'application/octet-stream' is not optimal. Most if not all browsers will simply download files with that type.
If you use proper MIME types (and inline Content-Disposition), browsers will have better default actions for some of them. Eg. in case of images, browsers will display them, which is probably what you'd want.
To deliver the file with the proper MIME type, the easiest way is to use:
header('Content-Type: ' . mime_content_type($file)); 
header('Content-Disposition: inline; filename="'.basename($file).'"');
My script working correctly on IE6 and Firefox 2 with any typ e of files (I hope :))
function DownloadFile($file) { // $file = include path 
    if(file_exists($file)) {
      header('Content-Description: File Transfer');
      header('Content-Type: application/octet-stream');
      header('Content-Disposition: attachment; filename='.basename($file));
      header('Content-Transfer-Encoding: binary');
      header('Expires: 0');
      header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
      header('Pragma: public');
      header('Content-Length: ' . filesize($file));
      ob_clean();
      flush();
      readfile($file);
      exit;
    }
  }
Run on Apache 2 (WIN32) PHP5
regarding php5:
i found out that there is already a disscussion @php-dev about readfile() and fpassthru() where only exactly 2 MB will be delivered.
so you may use this on php5 to get lager files 
To avoid the risk of choosing themselves which files to download by messing with the request and doing things like inserting "../" into the "filename", simply remember that URLs are not file paths, and there's no reason why the mapping between them has to be so literal as "download.php?file=thingy.mpg" resulting in the download of the file "thingy.mpg".
It's your script and you have full control over how it maps file requests to file names, and which requests retrieve which files.
But even then, as ever, never trust ANYTHING in the request. Basic first-day-at-school security principle, that.
To anyone that's had problems with Readfile() reading large files into memory the problem is not Readfile() itself, it's because you have output buffering on. Just turn off output buffering immediately before the call to Readfile(). Use something like ob_end_flush().
Send file with HTTPRange support (partial download):

Usage:

It can be slow for big files to read by fread, but this is a single way to read file in strict bounds. You can modify this and add fpassthru instead of fread and while, but it sends all data from begin --- it would be not fruitful if request is bytes from 100 to 200 from 100mb file.
A note on the smartReadFile function from gaosipov:
Change the indexes on the preg_match matches to:
   
   $begin = intval($matches[1]);
   if( !empty($matches[2]) ) {
    $end = intval($matches[2]);
   }
Otherwise the $begin would be set to the entire section matched and the $end to what should be the begin.
See preg_match for more details on this.
In response to flowbee@gmail.com --
When using the readfile_chunked function noted here with files larger than 10MB or so I am still having memory errors. It's because the writers have left out the all important flush() after each read. So this is the proper chunked readfile (which isn't really readfile at all, and should probably be crossposted to passthru(), fopen(), and popen() just so browsers can find this information):

All I've added is a flush(); after the echo line. Be sure to include this!
To avoid errors, 
just be careful whether slash "/" is allowed or not at the beginning of $file_name parameter.
In my case, trying to send PDF files thru PHP after access-logging,
the beginning "/" must be removed in PHP 7.1.
Instead of using

use

Some browsers have troubles with force-download.
If you are lucky enough to not be on shared hosting and have apache, look at installing mod_xsendfile.
This was the only way I found to both protect and transfer very large files with PHP (gigabytes). 
It's also proved to be much faster for basically any file.
Available directives have changed since the other note on this and XSendFileAllowAbove was replaced with XSendFilePath to allow more control over access to files outside of webroot.
Download the source.
Install with: apxs -cia mod_xsendfile.c
Add the appropriate configuration directives to your .htaccess or httpd.conf files:
# Turn it on
XSendFile on
# Whitelist a target directory.
XSendFilePath /tmp/blah
Then to use it in your script: 
A mime-type-independent forced download can also be conducted by using:

Cheers,
Peavey
Just a note: If you're using bw_mod (current version 0.6) to limit bandwidth in Apache 2, it *will not* limit bandwidth during readfile events.
If you are looking for an algorithm that will allow you to download (force download) a big file, may this one will help you.
$filename = "file.csv";
$filepath = "/path/to/file/" . $filename;
// Close sessions to prevent user from waiting until 
// download will finish (uncomment if needed)
//session_write_close();
set_time_limit(0);
ignore_user_abort(false);
ini_set('output_buffering', 0);
ini_set('zlib.output_compression', 0);
$chunk = 10 * 1024 * 1024; // bytes per chunk (10 MB)
$fh = fopen($filepath, "rb");
if ($fh === false) { 
  echo "Unable open file"; 
}
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"'); 
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
// Repeat reading until EOF
while (!feof($fh)) { 
  echo fread($handle, $chunk);
  
  ob_flush(); // flush output
  flush();
}
exit;
here is a nice force download scirpt
      $filename = 'dummy.zip';
      $filename = realpath($filename);
      $file_extension = strtolower(substr(strrchr($filename,"."),1));
      switch ($file_extension) {
        case "pdf": $ctype="application/pdf"; break;
        case "exe": $ctype="application/octet-stream"; break;
        case "zip": $ctype="application/zip"; break;
        case "doc": $ctype="application/msword"; break;
        case "xls": $ctype="application/vnd.ms-excel"; break;
        case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
        case "gif": $ctype="image/gif"; break;
        case "png": $ctype="image/png"; break;
        case "jpe": case "jpeg":
        case "jpg": $ctype="image/jpg"; break;
        default: $ctype="application/force-download";
      }
      if (!file_exists($filename)) {
        die("NO FILE HERE");
      }
      header("Pragma: public");
      header("Expires: 0");
      header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
      header("Cache-Control: private",false);
      header("Content-Type: $ctype");
      header("Content-Disposition: attachment; filename=\"".basename($filename)."\";");
      header("Content-Transfer-Encoding: binary");
      header("Content-Length: ".@filesize($filename));
      set_time_limit(0);
      @readfile("$filename") or die("File not found.");
In the C source, this function simply opens the path in read+binary mode, without a lock, and uses fpassthru()
If you need a locked read, use fopen(), flock(), and then fpassthru() directly.
Using pieces of the forced download script, adding in MySQL database functions, and hiding the file location for security was what we needed for downloading wmv files from our members creations without prompting Media player as well as secure the file itself and use only database queries. Something to the effect below, very customizable for private access, remote files, and keeping order of your online media.

The \ "before and after the file name makes the difference.
If you are using the procedures outlined in this article to force sending a file to a user, you may find that the "Content-Length" header is not being sent on some servers.
The reason this occurs is because some servers are setup by default to enable gzip compression, which sends an additional header for such operations. This additional header is "Transfer-Encoding: chunked" which essentially overrides the "Content-Length" header and forces a chunked download. Of course, this is not required if you are using the intelligent versions of readfile in this article. 
A missing Content-Length header implies the following:
1) Your browser will not show a progress bar on downloads because it doesn't know their length
2) If you output anything (e.g. white space) after the readfile function (by mistake), the browser will add that to the end of the download, resulting in corrupt data.
The easiest way to disable this behaviour is with the following .htaccess directive.
SetEnv no-gzip dont-vary
Remember if you make a "force download" script like mentioned below that you SANITIZE YOUR INPUT!
I have seen a lot of download scripts that does not test so you are able to download anything you want on the server.
Test especially for strings like ".." which makes directory traversal possible. If possible only permit characters a-z, A-Z and 0-9 and make it possible to only download from one "download-folder".
Beware - the chunky readfile suggested by Rob Funk can easily exceed you maximum script execution time (30 seconds by default).
I suggest you to use the set_time_limit function inside the while loop to reset the php watchdog.
In reply to herbert dot fischer at NOSPAM dot gmail dot com:
The streams API in PHP5 tries to make things as efficient as possible; in php-5.1.6 on Linux, fpassthru is faster than 'echo fread($fp, 8192)' in a loop, and readfile is even faster for files on disk. I didn't benchmark further, but I'd be willing to bet non-mmap'able streams still win because they can loop in C instead of PHP.
In response to "grey - greywyvern - com":
If you know the target _can't_ be a remote file (e.g. prefixing it with a directory), you should use include instead.
If the user manages to set the target to some kinda config-file (configuration.php in Joomla!), he will get a blank page - unless readfile() is used. Using include will just behave as a normal request (no output).
For remote files however use readfile().
@Elliott Brueggeman
What's the point of a user's settings if not to determine their environment? If they have it set a specific way, honor their setting.
To use readfile() it is absolutely necessary to set the mime-type before. If you are using an Apache, it's quite simple to figure out the correct mime type. Apache has a file called "mime.types" which can (in normal case) be read by all users.
Use this (or another) function to get a list of mime-types:

If you do not want to read from the mime.types file directly, you can of course make a copy in another folder!
Cheers Philipp Heckel
Beware of using download managers.. I was trying to use readfile in IE8 and kept getting the message "failed to get data for 'type'". Eventually figured out the problem was that I had LeechGet installed and it was intercepting the download, which in turn prevented the download from taking place.

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)