文字

fopen

(PHP 4, PHP 5, PHP 7)

fopen打开文件或者 URL

说明

resource fopen ( string $filename , string $mode [, bool $use_include_path = false [, resource $context ]] )

fopen() filename 指定的名字资源绑定到一个流上。

参数

filename

如果 filename 是 "scheme://..." 的格式,则被当成一个 URL,PHP 将搜索协议处理器(也被称为封装协议)来处理此模式。如果该协议尚未注册封装协议,PHP 将发出一条消息来帮助检查脚本中潜在的问题并将 filename 当成一个普通的文件名继续执行下去。

如果 PHP 认为 filename 指定的是一个本地文件,将尝试在该文件上打开一个流。该文件必须是 PHP 可以访问的,因此需要确认文件访问权限允许该访问。如果激活了安全模式或者 open_basedir 则会应用进一步的限制。

如果 PHP 认为 filename 指定的是一个已注册的协议,而该协议被注册为一个网络 URL,PHP 将检查并确认 allow_url_fopen 已被激活。如果关闭了,PHP 将发出一个警告,而 fopen 的调用则失败。

Note:

所支持的协议列表见支持的协议和封装协议。某些协议(也被称为 wrappers)支持 context 和/或 php.ini 选项。参见相应的页面哪些选项可以被设定(例如 php.ini 中用于 http wrapper 的 user_agent 值)。

On the Windows platform, be careful to escape any backslashes used in the path to the file, or use forward slashes.

<?php
$handle 
fopen ( "c:\\folder\\resource.txt" "r" );
?>
mode

mode 参数指定了所要求到该流的访问类型。可以是以下:

fopen() mode 的可能值列表
mode 说明
'r' 只读方式打开,将文件指针指向文件头。
'r+' 读写方式打开,将文件指针指向文件头。
'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
'x' 创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,则 fopen() 调用失败并返回 FALSE ,并生成一条 E_WARNING 级别的错误信息。如果文件不存在则尝试创建之。这和给 底层的 open(2) 系统调用指定 O_EXCL|O_CREAT 标记是等价的。
'x+' 创建并以读写方式打开,其他的行为和 'x' 一样。
'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer is positioned on the beginning of the file. This may be useful if it's desired to get an advisory lock (see flock() ) before attempting to modify the file, as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can be used after the lock is requested).
'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'.

Note:

不同的操作系统家族具有不同的行结束习惯。当写入一个文本文件并想插入一个新行时,需要使用符合操作系统的行结束符号。基于 Unix 的系统使用 \n 作为行结束字符,基于 Windows 的系统使用 \r\n 作为行结束字符,基于 Macintosh 的系统使用 \r 作为行结束字符。

如果写入文件时使用了错误的行结束符号,则其它应用程序打开这些文件时可能会表现得很怪异。

Windows 下提供了一个文本转换标记('t')可以透明地将 \n 转换为 \r\n。与此对应还可以使用 'b' 来强制使用二进制模式,这样就不会转换数据。要使用这些标记,要么用 'b' 或者用 't' 作为 mode 参数的最后一个字符。

默认的转换模式依赖于 SAPI 和所使用的 PHP 版本,因此为了便于移植鼓励总是指定恰当的标记。如果是操作纯文本文件并在脚本中使用了 \n 作为行结束符,但还要期望这些文件可以被其它应用程序例如 Notepad 读取,则在 mode 中使用 't'。在所有其它情况下使用 'b'

在操作二进制文件时如果没有指定 'b' 标记,可能会碰到一些奇怪的问题,包括坏掉的图片文件以及关于 \r\n 字符的奇怪问题。

Note:

为移植性考虑,强烈建议在用 fopen() 打开文件时总是使用 'b' 标记。

Note:

再一次,为移植性考虑,强烈建议你重写那些依赖于 't' 模式的代码使其使用正确的行结束符并改成 'b' 模式。

use_include_path

如果也需要在 include_path 中搜寻文件的话,可以将可选的第三个参数 use_include_path 设为 '1' 或 TRUE

context

Note: 在 PHP 5.0.0 中增加了对上下文(Context)的支持。有关上下文(Context)的说明参见 Streams。

返回值

成功时返回文件指针资源,如果打开失败,本函数返回 FALSE

错误/异常

如果打开失败,会产生一个 E_WARNING 错误。可以通过 @ 来屏蔽错误。

更新日志

版本 说明
4.3.2 自 PHP 4.3.2 起,对所有区别二进制和文本模式的平台默认模式都被设为二进制模式。如果在升级后脚本碰到问题,尝试暂时使用 't' 标记,直到所有的脚本都照以下所说的改为更具移植性以后。
4.3.2 增加了选项 'x''x+'
5.2.6 增加了选项 'c''c+'

范例

Example #1 fopen() 例子

<?php
$handle 
fopen ( "/home/rasmus/file.txt" "r" );
$handle  fopen ( "/home/rasmus/file.gif" "wb" );
$handle  fopen ( "http://www.example.com/" "r" );
$handle  fopen ( "ftp://user:password@example.com/somefile.txt" "w" );
?>

注释

Warning

使用 SSL 时,Microsoft IIS 会违反协议不发送close_notify标记就关闭连接。PHP 会在到达数据尾端时报告“SSL: Fatal Protocol Error”。 要解决此问题,error_reporting 应设定为降低级别至不包含警告。 PHP 4.3.7 及更高版本可以在使用 https:// 包装器打开流时检测出有问题的 IIS 服务器软件 并抑制警告。在使用 fsockopen() 创建 ssl:// 套接字时, 开发者需检测并抑制此警告。

Note: 当启用 安全模式时, PHP 会在执行脚本时检查被脚本操作的目录是否与被执行的脚本有相同的 UID(所有者)。

Note:

如果在用服务器模块版本的 PHP 时在打开和写入文件上遇到问题,记住要确保所使用的文件和目录是服务器进程所能够访问的。

Note:

This function may also succeed when filename is a directory. If you are unsure whether filename is a file or a directory, you may need to use the is_dir() function before calling fopen() .

参见

  • 支持的协议和封装协议
  • fclose() - 关闭一个已打开的文件指针
  • fgets() - 从文件指针中读取一行
  • fread() - 读取文件(可安全用于二进制文件)
  • fwrite() - 写入文件(可安全用于二进制文件)
  • fsockopen() - 打开一个网络连接或者一个Unix套接字连接
  • file() - 把整个文件读入一个数组中
  • file_exists() - 检查文件或目录是否存在
  • is_readable() - 判断给定文件名是否可读
  • stream_set_timeout() - Set timeout period on a stream
  • popen() - 打开进程文件指针
  • stream_context_create() - 创建资源流上下文
  • umask() - 改变当前的 umask
  • SplFileObject

用户评论:

[#1] lp dot soni at yahoo dot co dot in [2015-10-11 07:12:23]

For the directory separators use the PHP constant DIRECTORY_SEPARATOR.

[#2] KOmaSHOOTER at gxm dot de [2015-03-25 11:46:07]

Check for UTF-8 content in fread after fopen:
For checking the UTF-8 content I use the function 'htmlentities();'
Example:
<?php
$filename 
"CONTENT.htm";
$handle fopen($filename"r");
$contents fread($handlefilesize($filename));
// Check for UTF-8 content
if(htmlentities(utf8_decode($contents))){
    
$contents htmlentities(utf8_decode($contents));
}else{
    
$contents htmlentities($contents);
}
?>

[#3] qrworld.net [2014-11-11 08:02:28]

Here you have a function found on the website http://softontherocks.blogspot.com/2014/11/funcion-para-escribir-en-un-fichero-log.html with an example of how to make a log file.

The function is this:

function writeLog($data) {
 list($usec, $sec) = explode(' ', microtime());
 $datetime = strftime("%Y%m%d %H:%M:%S",time());
 $msg = "$datetime'". sprintf("%06s",intval($usec*1000000)).": $data";
 $save_path = 'foo.txt';
 $fp = @fopen($save_path, 'a'); // open or create the file for writing and append info
 fputs($fp, "$msg\n"); // write the data in the opened file
 fclose($fp); // close the file
}

[#4] Antoine [2014-09-04 15:55:00]

On a Windows webserver, when using fopen with a file path stored in a variable, PHP will return an error if the variable isn't encoded in ASCII, which may be the case if the file file path is retrieved from a database.

Possible workaround :
<?php
$encoding 
mb_detect_encoding($filePath);
$filePath mb_convert_encoding($filePath"ASCII"$encoding);
$filePath str_replace("?"""$filePath);
$filePath addslashes($filePath);

if(
file_exists($filePath)) {
    echo 
"File Found.";
    
$handle       fopen($filePath"r");
    
$fileContents fread($handlefilesize($filePath));
    
fclose($handle);
    if(!empty(
$fileContents)) {
        echo 
"<pre>".$fileContents."</pre>";
    }
}
else {
    echo 
"File Not Found.";
}
?>

[#5] contact at sergeylukin dot com [2013-08-21 11:53:58]

Here is how you could modify file's contents by only opening it once in "r+" mode:

<?php
$filePath 
'/path/to/file';
if( !@
$fh fopen($filePath'r+') ) {
  die(
"Could not open $filePath in READ+WRITE mode");
}

// Extract contents
$contents fread($fhfilesize($filePath));

// ..modify $contents in any way you need..

// Truncate file (important to move the pointer first)
fseek($fh0);
ftruncate($fh0);

// Write
fwrite($fh$contents);

fclose($fh);
?>

[#6] simon dot riget at gamil dot com [2013-05-21 18:50:05]

Writing and reading on a serial port.

If you are unable or unwilling to install the serial device library for PHP, its still possible to communicate through a serial port or USB device.

There are two issues to note:
- you must use a system call to set the port control options
- you must use NON blocking stream mode for reading (not for writing unless you use flow control) 

<?php
// Set timeout to 500 ms
$timeout=microtime(true)+0.5;

// Set device controle options (See man page for stty)
exec("/bin/stty -F /dev/ttyS0 19200 sane raw cs8 hupcl cread clocal -echo -onlcr ");
    
// Open serial port
$fp=fopen("/dev/ttyS0","c+");
if(!
$fp) die("Can't open device");

// Set blocking mode for writing
stream_set_blocking($fp,1);
fwrite($fp,"foo\n");

// Set non blocking mode for reading
stream_set_blocking($fp,0);
do{
  
// Try to read one character from the device
  
$c=fgetc($fp);
 
  
// Wait for data to arive 
  
if($c === false){
      
usleep(50000);
      continue;
  }  
  
  
$line.=$c;
    
}while(
$c!="\n" && microtime(true)<$timeout); 
  
echo 
"Responce: $line";  
?>

[#7] simon dot riget at gmail dot com [2013-04-02 21:41:14]

The following is an  example of file locking used in a function to share an array between processes.
In this example the function reads the array, possibly remove stuff like spend semaphores and insert new stuff to share.
The file gets locked for writing thus making sure that two processes doesn't inadvertently delete each others data.

The function can be called multiple times during a script, which becomes increasingly important if you write HTML5 Server Sent Events and websockets apps, since the script dose not end when the page are loaded. (there are other issues with that like garbage collection and memory leaks)
Using the ??c?? mode of fopen, removes the (remote) change of a race condition to the file lock.

Be aware that the function filesize() is not working correctly when files are in cash memory. You could flush the cash or in this case, since the file size of IPC are neglectable, just give a very large number as length parameter to fread.

<?php
function ipc($new_record=""){
  
$update=false;

  
// Open file with locking
  
$handle=fopen("example.dat","c+");
  
  
// Lock the file for writing
  
flock($handle,LOCK_EX);

  
// Get file size. 
  // you can either fluch the file cash to get an acurate number
  // clearstatcache();
  // Or use a large number    

  // Load event file into array
  
$contents=fread($handle,10000000);

  
$ipc_array=json_decode($contents,true);

  
// Clean out old stuff
  
if(is_array($ipc_array)){
    
$timeout=time()-60;  
    foreach(
$ipc_array as $key=>$record){
      
// eg remove spend semaphores or timed out messages
      
if( $record['time'] < $timeout ){
        unset(
$ipc_array[$key]);
        
$update=true;
      }
    }
  }

  
// Add new message
  
if(is_array($new_record)){
    
$new_record['time']=time();
    
$ipc_array[]=$new_record;
    
$update=true;
  }

  
// Write file and end
  
if($update){
    
// Overwrite the old data
    
ftruncate($handle,0);
    
rewind($handle);
  
    
// Write the new array to file
    
fwrite($handle,json_encode($ipc_array,JSON_NUMERIC_CHECK));
  }

  
// Unlock the file - you have to do it! it doesn't happen on fclose
  
flock($handle,LOCK_UN);

  
// Close the file      
  
fclose($handle);

  return 
$ipc_array;
}

// Read messages
$ipc=ipc();

// Show messages
foreach( $ipc as $record)
  echo 
$record['message'] ."<br>\n";

// Send a message
$message["message"]="hello world.";
$message["foo"]="bar";
$ipc=ipc($message);
?>

[#8] apathetic012 at gmail dot com [2012-09-04 15:19:25]

a variable $http_response_header is available when doing the fopen(). Which contains an array of the response header.

[#9] chapman at worldtakeoverindustries dot com [2012-03-09 19:25:11]

Note - using fopen in 'w' mode will NOT update the modification time (filemtime) of a file like you may expect. You may want to issue a touch() after writing and closing the file which update its modification time. This may become critical in a caching situation, if you intend to keep your hair.

[#10] kasper at webmasteren dot eu [2012-02-10 20:36:15]

"Do not use the following reserved device names for the name of a file:
CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, 
LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. Also avoid these names 
followed immediately by an extension; for example, NUL.txt is not recommended. 
For more information, see Namespaces"
it is a windows limitation.
see:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx

[#11] merye at web-graphique dot com [2011-08-19 13:10:43]

I had to switch from relative paths to full paths when my web host recently migrated the server onto their hosting cloud.  This used to work for me:

<?php
$fplog 
fopen('ipn.log','a');
?>


After the migration, however, I received the following error message:

Warning: fopen(ipn.log) [function.fopen]: failed to open stream: Permission denied in D:\home\server_name\log_test.php on line 21

I had to change the code to the following to get it to work:

<?php
$logfile 
$_SERVER['DOCUMENT_ROOT'].'\\ipn.log';
$fplog fopen($logfile,'a');
?>

[#12] php at richardneill dot org [2011-08-01 09:39:10]

fopen() will block if the file to be opened is a fifo. This is true whether it's opened in "r" or "w" mode.  (See man 7 fifo: this is the correct, default behaviour; although Linux supports non-blocking fopen() of a fifo, PHP doesn't).
The consequence of this is that you can't discover whether an initial fifo read/write would block because to do that you need stream_select(), which in turn requires that fopen() has happened!

[#13] splogamurugan at gmail dot com [2011-06-09 02:13:40]

While opening a file with multibyte data (Ex: donn??es multi-octets), faced some issues with the encoding. Got to know that it uses  windows-1250. Used iconv to convert it to UTF-8 and it resolved the issue.  

<?php
function utf8_fopen_read($fileName) {
    
$fc iconv('windows-1250''utf-8'file_get_contents($fileName)); 
    
$handle=fopen("php://memory""rw");
    
fwrite($handle$fc); 
    
fseek($handle0); 
    return 
$handle;
}
?>


Example usage:

<?php
$fh 
utf8_fopen_read("./tpKpiBundle.csv");
while ((
$data fgetcsv($fh1000",")) !== false) {
    foreach (
$data as $value) {
        echo 
$value "<br />\n";
    }
}
?>


Hope it helps.

[#14] owltech at larkandowl dot net [2011-02-06 23:20:14]

[fopen note]

I have been trying unsuccessfully to upload and read a Mac OS file on a Linux server. Lots of records show up a just one big using only the following:

<?php $fhandle fopen($file'r'); ?>
  or
<?php $fhandle fopen($file'rb'); ?>

It does work, however, this way:

<?php
ini_set
('auto_detect_line_endings'TRUE);
$fhandle fopen($file'r');
?>

[#15] Brad G [2010-10-16 14:10:40]

While adding CFLAGS="-D_FILE_OFFSET_BITS=64" immediately before calling "./configure" on the PHP source will enable support for using fopen() on large files (greater than 2 GB), note that -- if such an installation of PHP is used in conjunction with Apache HTTPD [2.x], Apache will become completely unresponsive even when not serving output from a PHP application.

In order to gain large file support for non-web applications while maintaining the operability of Apache, consider making two distinct PHP installations:  one with the above CFLAGS specified during configuration (for non-web uses), and the other without this flag (for use with Apache).

[#16] eyrie88 at gmail dot com [2010-07-31 19:39:26]

Be aware that fopen($url) also respects HTTP status headers. If the URL responds with a 1xx, 4xx, or 5xx status code, you will get a "failed to open stream: HTTP request failed!", followed by the HTTP status response. Same goes for file_get_contents($url)...

[#17] anfragen at tsgames dot de [2010-05-27 05:54:59]

Since the http-wrapper doesn't support stat() and so you can't use file_exists() for url's, you can simply use a function like this:

<?php
function http_file_exists($url)
{
$f=@fopen($url,"r");
if(
$f)
{
fclose($f);
return 
true;
}
return 
false;
}
?>

[#18] magnetik at magnetik dot org [2010-01-21 06:43:23]

There IS an option to use fopen with a proxy, it's in $context. 
No need to recode everything.

[#19] Pastix [2010-01-20 03:04:06]

If fopen() has been disabled for security reasons, is possible a porting FROM:

<?php
$f
=fopen($file,'rb');
$data='';
while(!
feof($f))
    
$data.=fread($f,$size);
fclose($f);
?>


TO:

<?php
$data 
file_get_contents($file); // (PHP 4 >= 4.3.0, PHP 5)
?>


and also a porting FROM:

<?php
$f 
fopen($file,'wb');
fwrite($f,$content,strlen($content));
fclose($f);
?>


TO:

<?php
$f
=file_put_contents($file$content); // (PHP 5)
?>


For detail read the php manual.

[#20] rene [2010-01-04 02:43:42]

if fopen() throws a E_WARNING "failed to open stream: HTTP request failed!" at you when opening a valid URL that you know returns data, i advise you to do the following before calling fopen($url,'r'):

<?php
ini_set 
('user_agent'$_SERVER['HTTP_USER_AGENT']);
?>


or anyways, set that 'user_agent' with ini_set() to something valid.

thanks, pollita|at|php.net @ http://bugs.php.net/bug.php?id=22937#c64196 , for the clue to this

[#21] b dot evieux at gmail dot com [2009-11-27 03:28:58]

Hello all. I have had trouble getting files through proxy, and the solution posted above only works if the requested file is also hosted on the proxy server (which is quite unlikely). so I've put together the following functions pfopen & preadfile. they quite work like the replaced fopen & readfile, and will resort to them if no proxy is provided :)

<?php
function preadfile($_url$_proxy_name null$_proxy_port 4480){
  if(
is_null($_proxy_name) || LOCAL_TEST){
    return 
readfile($_url);
  }else{
    
$proxy_cont '';

    
$proxy_fp pfopen($_url$_proxy_name$_proxy_port);
    while(!
feof($proxy_fp)) {$proxy_cont .= fread($proxy_fp,4096);}
    
fclose($proxy_fp);

    
$proxy_cont substr($proxy_contstrpos($proxy_cont,"\r\n\r\n")+4);
    echo 
$proxy_cont;
    return 
count($proxy_cont);
  }
}
function 
pfopen($_url$_proxy_name null$_proxy_port 4480) {
  if(
is_null($_proxy_name) || LOCAL_TEST){
    return 
fopen($_url);
  }else{
    
$proxy_fp fsockopen($_proxy_name$_proxy_port);
    if (!
$proxy_fp) return false;
    
$hostsubstr($_url7);
    
$host substr($bucket0strpos($host"/"));

    
$request "GET $_url HTTP/1.0\r\nHost:$host\r\n\r\n";

    
fputs($proxy_fp$request);

    return 
$proxy_fp;
  }
}
?>

[#22] gmdebby at gmail dot com [2009-06-08 06:49:20]

I was wondering why was added the "x" mode, it works only if the file do not exists, will create it and open it in read only, it's useless !!
But I found something I could do with that.

Here is a little mk_file function.

<?php
function mk_file($filename) {
    if(!
is_file($filename)) {
        
fclose(fopen($filename,"x")); //create the file and close it
        
return true;
    } else return 
false//file already exists
}
?>


You can improve it, add chmod support etc...

[#23] php at delhelsa dot com [2008-06-24 12:27:57]

With php 5.2.5 on Apache 2.2.4, accessing files on an ftp server with fopen() or readfile() requires an extra forwardslash if an absolute path is needed.

i.e., if a file called bullbes.txt is stored under /var/school/ on ftp server example.com and you're trying to access it with user blossom and password buttercup, the url would be:

ftp://blossom:buttercup@example.com//var/school/bubbles.txt

Note the two forwardslashes. It looks like the second one is needed so the server won't interpret the path as relative to blossom's home on townsville.

[#24] webmaster at myeshop dot fr [2008-04-26 07:40:15]

Also a small function useful for backup for example. It's a mixed between the fopen() and the mkdir() functions.

This function opens a file but also make the path recursively where the file is contained. This is helpful for ending to finish with "No such file or directory in" errors

<?php
function fopen_recursive($path$mode$chmod=0755){
  
preg_match('`^(.+)/([a-zA-Z0-9]+\.[a-z]+)$`i'$path$matches);
  
$directory $matches[1];
  
$file $matches[2];

  if (!
is_dir($directory)){
    if (!
mkdir($directory$chmod1)){
    return 
FALSE;
    }
  }
 return 
fopen ($path$mode);
}
?>

[#25] jphansen at uga dot edu [2008-02-22 07:04:17]

If you open a file with r+ and execute an fwrite(), writing less to the file than what it originally was, it will result in the difference being padded with the end of the file from the previous end of the file. Example:

<?php
// Open file for read and string modification
$file "/test";
$fh fopen($file'r+');
$contents fread($fhfilesize($file));
$new_contents str_replace("hello world""hello"$contents);
fclose($fh);

// Open file to write
$fh fopen($file'r+');
fwrite($fh$new_contents);
fclose($fh);
?>


If the end of the file was "abcdefghij", you will notice that the difference in "hello world" and "hello", 6 characters, will be appended to the file, resulting in the new ending: "efghij". To obviate this, fopen() with +w instead, which truncates the file to zero length.

[#26] sean downey [2008-02-09 00:23:30]

when using ssl / https on windows i would get the error:
"Warning: fopen(https://example.com): failed to open stream: Invalid argument in someSpecialFile.php on line 4344534"

This was because I did not have the extension "php_openssl.dll" enabled.

So if you have the same problem, goto your php.ini file and enable it :)

[#27] info at NOSPAMPLEASE dot c-eagle dot com [2007-10-08 18:14:14]

If there is a file that?s excessively being rewritten by many different users, you?ll note that two almost-simultaneously accesses on that file could interfere with each other. For example if there?s a chat history containing only the last 25 chat lines. Now adding a line also means deleting the very first one. So while that whole writing is happening, another user might also add a line, reading the file, which, at this point, is incomplete, because it?s just being rewritten. The second user would then rewrite an incomplete file and add its line to it, meaning: you just got yourself some data loss!

If flock() was working at all, that might be the key to not let those interferences happen - but flock() mostly won?t work as expected (at least that?s my experience on any linux webserver I?ve tried), and writing own file-locking-functions comes with a lot of possible issues that would finally result in corrupted files. Even though it?s very unlikely, it?s not impossible and has happened to me already.

So I came up with another solution for the file-interference-problem:

1. A file that?s to be accessed will first be copied to a temp-file directory and its last filemtime() is being stored in a PHP-variable. The temp-file gets a random filename, ensuring no other process is able to interfere with this particular temp-file.
2. When the temp-file has been changed/rewritten/whatever, there?ll be a check whether the filemtime() of the original file has been changed since we copied it into our temp-directory.
2.1. If filemtime() is still the same, the temp-file will just be renamed/moved to the original filename, ensuring the original file is never in a temporary state - only the complete previous state or the complete new state.
2.2. But if filemtime() has been changed while our PHP-process wanted to change its file, the temp-file will just be deleted and our new PHP-fileclose-function will return a FALSE, enabling whatever called that function to do it again (ie. upto 5 times, until it returns TRUE).

These are the functions I?ve written for that purpose:

<?php
$dir_fileopen 
"../AN/INTERNAL/DIRECTORY/fileopen";

function 
randomid() {
    return 
time().substr(md5(microtime()), 0rand(512));
}

function 
cfopen($filename$mode$overwriteanyway false) {
    global 
$dir_fileopen;
    
clearstatcache();
    do {
        
$id md5(randomid(rand(), TRUE));
        
$tempfilename $dir_fileopen."/".$id.md5($filename);
    } while(
file_exists($tempfilename));
    if (
file_exists($filename)) {
        
$newfile false;
        
copy($filename$tempfilename);
    }else{
        
$newfile true;
    }
    
$fp fopen($tempfilename$mode);
    return 
$fp ? array($fp$filename$id, @filemtime($filename), $newfile$overwriteanyway) : false;
}

function 
cfwrite($fp,$string) { return fwrite($fp[0], $string); }

function 
cfclose($fp$debug "off") {
    global 
$dir_fileopen;
    
$success fclose($fp[0]);
    
clearstatcache();
    
$tempfilename $dir_fileopen."/".$fp[2].md5($fp[1]);
    if ((@
filemtime($fp[1]) == $fp[3]) or ($fp[4]==true and !file_exists($fp[1])) or $fp[5]==true) {
        
rename($tempfilename$fp[1]);
    }else{
        
unlink($tempfilename);
        if (
$debug != "off") echo "While writing, another process accessed $fp[1]. To ensure file-integrity, your changes were rejected.";
        
$success false;
    }
    return 
$success;
}
?>


$overwriteanyway, one of the parameters for cfopen(), means: If cfclose() is used and the original file has changed, this script won?t care and still overwrite the original file with the new temp file. Anyway there won?t be any writing-interference between two PHP processes, assuming there can be no absolute simultaneousness between two (or more) processes.

[#28] simon dot allen at swerve dot co dot nz [2007-04-09 19:56:57]

using fopen to upload a file through ftp cannot overwrite that file - use curl instead

[#29] ceo at l-i-e dot com [2006-04-10 20:13:13]

If you need fopen() on a URL to timeout, you can do like:
<?php
  $timeout 
3;
  
$old ini_set('default_socket_timeout'$timeout);
  
$file fopen('http://example.com''r');
  
ini_set('default_socket_timeout'$old);
  
stream_set_timeout($file$timeout);
  
stream_set_blocking($file0);
  
//the rest is standard
?>

[#30] flobee [2006-01-14 19:58:48]

download: i need a function to simulate a "wget url" and do not buffer the data in the memory to avoid thouse problems on large files:
<?php
function download($file_source$file_target) {
        
$rh fopen($file_source'rb');
        
$wh fopen($file_target'wb');
        if (
$rh===false || $wh===false) {
// error reading or opening file
           
return true;
        }
        while (!
feof($rh)) {
            if (
fwrite($whfread($rh1024)) === FALSE) {
                   
// 'Download error: Cannot write to file ('.$file_target.')';
                   
return true;
               }
        }
        
fclose($rh);
        
fclose($wh);
        
// No error
        
return false;
    }
?>

[#31] info at b1g dot de [2005-10-24 04:54:17]

Simple class to fetch a HTTP URL. Supports "Location:"-redirections. Useful for servers with allow_url_fopen=false. Works with SSL-secured hosts.

<?php
#usage:
$r = new HTTPRequest('http://www.example.com');
echo 
$r->DownloadToString();

class 
HTTPRequest
{
    var 
$_fp;        // HTTP socket
    
var $_url;        // full URL
    
var $_host;        // HTTP host
    
var $_protocol;    // protocol (HTTP/HTTPS)
    
var $_uri;        // request URI
    
var $_port;        // port
    
    // scan url
    
function _scan_url()
    {
        
$req $this->_url;
        
        
$pos strpos($req'://');
        
$this->_protocol strtolower(substr($req0$pos));
        
        
$req substr($req$pos+3);
        
$pos strpos($req'/');
        if(
$pos === false)
            
$pos strlen($req);
        
$host substr($req0$pos);
        
        if(
strpos($host':') !== false)
        {
            list(
$this->_host$this->_port) = explode(':'$host);
        }
        else 
        {
            
$this->_host $host;
            
$this->_port = ($this->_protocol == 'https') ? 443 80;
        }
        
        
$this->_uri substr($req$pos);
        if(
$this->_uri == '')
            
$this->_uri '/';
    }
    
    
// constructor
    
function HTTPRequest($url)
    {
        
$this->_url $url;
        
$this->_scan_url();
    }
    
    
// download URL to string
    
function DownloadToString()
    {
        
$crlf "\r\n";
        
        
// generate request
        
$req 'GET ' $this->_uri ' HTTP/1.0' $crlf
            
.    'Host: ' $this->_host $crlf
            
.    $crlf;
        
        
// fetch
        
$this->_fp fsockopen(($this->_protocol == 'https' 'ssl://' '') . $this->_host$this->_port);
        
fwrite($this->_fp$req);
        while(
is_resource($this->_fp) && $this->_fp && !feof($this->_fp))
            
$response .= fread($this->_fp1024);
        
fclose($this->_fp);
        
        
// split header and body
        
$pos strpos($response$crlf $crlf);
        if(
$pos === false)
            return(
$response);
        
$header substr($response0$pos);
        
$body substr($response$pos strlen($crlf));
        
        
// parse headers
        
$headers = array();
        
$lines explode($crlf$header);
        foreach(
$lines as $line)
            if((
$pos strpos($line':')) !== false)
                
$headers[strtolower(trim(substr($line0$pos)))] = trim(substr($line$pos+1));
        
        
// redirection?
        
if(isset($headers['location']))
        {
            
$http = new HTTPRequest($headers['location']);
            return(
$http->DownloadToString($http));
        }
        else 
        {
            return(
$body);
        }
    }
}
?>

[#32] admin at sellchain dot com [2005-10-17 07:34:05]

TIP: If you are using fopen and fread to read HTTP or FTP or Remote Files, and experiencing some performance issues such as stalling, slowing down and otherwise, then it's time you learned a thing called cURL.

Performance Comparison:

10 per minute for fopen/fread for 100 HTTP files
2000 per minute for cURL for 2000 HTTP files

cURL should be used for opening HTTP and FTP files, it is EXTREMELY reliable, even when it comes to performance.

I noticed when using too many scripts at the same time to download the data from the site I was harvesting from, fopen and fread would go into deadlock. When using cURL i can open 50 windows, running 10 URL's from each window, and getting the best performance possible. 

Just a Tip :)

[#33] nefertari at nefertari dot be [2005-09-20 05:47:58]

Important note:

You have always to use the real path name for a file with the command fopen [for example: fopen($filename, 'w')], never use a symbolic link, it will not work (unable to open $filename).

[#34] durwood at speakeasy dot NOSPAM dot net [2005-09-06 23:43:48]

I couldn't for the life of me get a certain php script working when i moved my server to a new Fedora 4 installation. The problem was that fopen() was failing when trying to access a file as a URL through apache -- even though it worked fine when run from the shell and even though the file was readily readable from any browser.  After trying to place blame on Apache, RedHat, and even my cat and dog, I finally ran across this bug report on Redhat's website:

https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=164700

Basically the problem was SELinux (which I knew nothing about) -- you have to run the following command in order for SELinux to allow php to open a web file:

/usr/sbin/setsebool httpd_can_network_connect=1

To make the change permanent, run it with the -P option:

/usr/sbin/setsebool -P httpd_can_network_connect=1

Hope this helps others out -- it sure took me a long time to track down the problem.

[#35] Luiz Miguel Axcar (lmaxcar at yahoo dot com dot br) [2005-08-17 13:11:12]

If you are getting message "Warning: fopen(): URL file-access is disabled in the server configuration", you can use function below to get the content from a local or remote file.

Function uses CURL lib, follow the link to get help: http://www.php.net/curl

<?php


function get_content($url)
{
    
$ch curl_init();

    
curl_setopt ($chCURLOPT_URL$url);
    
curl_setopt ($chCURLOPT_HEADER0);

    
ob_start();

    
curl_exec ($ch);
    
curl_close ($ch);
    
$string ob_get_contents();

    
ob_end_clean();
    
    return 
$string;     
}

#usage:
$content get_content ("http://www.php.net");
var_dump ($content);
?>

[#36] ideacode [2005-08-03 07:28:04]

Note that whether you may open directories is operating system dependent. The following lines:

<?php
// Windows ($fh === false)
$fh fopen('c:\\Temp''r');

// UNIX (is_resource($fh) === true)
$fh fopen('/tmp''r');
?>


demonstrate that on Windows (2000, probably XP) you may not open a directory (the error is "Permission Denied"), regardless of the security permissions on that directory.

On UNIX, you may happily read the directory format for the native filesystem.

[#37] Thomas Candrian tc_ at gmx dot ch [2004-11-11 19:35:43]

With this it isn't possible to get data from another port than 80 (and 443) - at least for me. Because of that I've made this function who gets data from every port you want using HTTP:

<?php;
function getcontent($server, $port, $file)
{
    $cont = "";
    $ip = gethostbyname($server);
    $fp = fsockopen($ip, $port);
    if (!$fp)
    {
        return "Unknown";
    }
    else
    {
        $com = "GET $file HTTP/1.1\r\nAccept: *
    
while(!feof($fp)) {
        
$buf fread($fp4096);
        echo 
$buf;
        
$bytesSent+=strlen($buf);    
    
}
?>


I've then got this code to update my database to say that the file was downloaded successfully.

<?php
    
if($bytesSent==filesize($file)) {
        

    
}
?>

[#10] nexz2004 at yahoo dot com [2004-05-21 00:30:50]

also it is possible to make your php script resume downloads, to do this you need to check $_SERVER['HTTP_RANGE'] which may contain something like this
 "bytes=10-" - resume from position 10, and to end of file

when sending response it is also needed to send with headers
Accept-Ranges: bytes
Content-Length: {filesize}
Content-Range: bytes 10-{filesize-1}/{ffilesize}

hope its usefull

[#11] axx at axxess dot ca [2004-02-15 13:04:05]

I have also perused this list of examples which I am sure work for that person, but, as others have mentioned here, do not work for me or (anyone else).

So what I did was try out all of these examples, check other sources of information, and put together what I think to be an example of what works on 'more than a few' systems.  The following example works for me wherever I need to create a download using fpassthru(), which works with IE6 (among other browsers):

<?php

$fileDir "/home/pathto/myfiles"// supply a path name.
$fileName "myfile.zip"// supply a file name.
$fileString=$fileDir.'/'.$fileName// combine the path and file
// translate file name properly for Internet Explorer.
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")){
  
$fileName preg_replace('/\./''%2e'$fileNamesubstr_count($fileName'.') - 1);
}
// make sure the file exists before sending headers
if(!$fdl=@fopen($fileString,'r')){
    die(
"Cannot Open File!");
} else {
  
header("Cache-Control: ");// leave blank to avoid IE errors
  
header("Pragma: ");// leave blank to avoid IE errors
  
header("Content-type: application/octet-stream");
  
header("Content-Disposition: attachment; filename=\"".$fileName."\"");
  
header("Content-length:".(string)(filesize($fileString)));
    
sleep(1);
    
fpassthru($fdl);
}
?>


All that should require editing is the $fileDir and $fileName variables.  Upload the file and point to it with your browser to see if the script will prompt you for a download.

NOTE : Regarding File Types : Leaving the 'Content-type' header as-is should allow you to download pretty much any file.  I have tested it on some of the more popular file types including zip, css, php, inc, htm, png, gif and jpg.  During these tests, I did note that if I selected 'cancel' or 'open' when prompted to download either a gif or jpg, that it would indeed cancel or open in my image browser as it should, but subsequent attempts at 'downloading only' yielded a web page view of the image.  Closing the window and opening a new one reset this, allowing me save a jpeg or gif to the hard drive directly.  I believe the problem lies in the way the caching headers are treated, since if any info is specified in the 'cache-control' header, the browser download fails completely (in IE, anyways).

Enjoy! Mail me if it works!  ;-)

[#12] mm at tbwachiat dot com [2004-01-16 17:00:40]

I've tried all of these renditions of this elusive task.  NONE of them have worked for me.  And when i say work, i mean where i can click some sort of link and have a file Save As... dialog box come up on MSIE 6.0.  In every other browser i've tried (Safari,Firebird,Netscape pc and mac) all have worked where it downloads to my desktop or asks me to save it in a certain place.

on MSIE 6.0.  the file i'm trying to download appears in it's own window. it's an image. BUT, the only thing i can do with it is SAVE IT AS A BMP. ugh.

I'm using the fpassthru function because i have files that must not be served by the webserver.

[#13] The Otter [2004-01-10 09:57:04]

In reply to spam at flatwan dot net
This might save someone some time. I created a program to list some rather large files and create links for the end user to click on in order to download them (using the php function fpassthru()).

The problem I was having was it would make it half way through the download (about 377 megs) and the script would terminate and the download would stop.

After doing some shotgun troubleshooting I discovered the php config option 'max_execution_time = 30'. Upon changing it to 'max_execution_time = -1' the files >370 megs can be downloaded without the script aborting.

The best way to do this would to be:
<?php
@ignore_user_abort();
@
set_time_limit(0);
?>

This only changes these settings for the script that calls them. (Thanks to (I don't remember who) who wrote a form mail script that used these two lines)

[#14] spam at flatwan dot net [2003-11-18 16:07:29]

This might save someone some time. I created a program to list some rather large files and create links for the end user to click on in order to download them (using the php function fpassthru()).

The problem I was having was it would make it half way through the download (about 377 megs) and the script would terminate and the download would stop.

After doing some shotgun troubleshooting I discovered the php config option 'max_execution_time = 30'. Upon changing it to 'max_execution_time = -1' the files >370 megs can be downloaded without the script aborting.

Jon

[#15] arabold AT nero DOT com [2003-05-15 04:11:26]

Here's a summary the different headers you need to set to make downloads *always* work with IE and Mozilla:

[SNIP]
  $disposition = "inline"; // "inline" to view file in browser or "attachment" to download to hard disk
  $mime = "image/jpeg"; // or whatever the mime type is
  $name = "foo.jpg"; // file name
  $path = "/path/to/foo.jpg"; // full path and file name
 
  if (isset($_SERVER["HTTPS"])) {
      
      header("Pragma: ");
      header("Cache-Control: ");
      header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
      header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
      header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
      header("Cache-Control: post-check=0, pre-check=0", false);
  }
      else if ($disposition == "attachment") {
          header("Cache-control: private");
  }
  else {
      header("Cache-Control: no-cache, must-revalidate");
      header("Pragma: no-cache");
  }
  header("Content-Type: $mime");
  header("Content-Disposition:$disposition; filename=\"".trim(htmlentities($name))."\"");
  header("Content-Description: ".trim(htmlentities($name)));
  header("Content-Length: ".(string)(filesize($path)));
  header("Connection: close");
[/SNIP]

This way all kinds of download work for me. Hope that helps

[#16] DarkAngela_ at hotmail dot com [2003-05-08 04:48:49]

Just a little thing more from the ssharma's script (thx to him for his great help ...) :

Don't forget to put the fopen with the "rb" argument and not just with the "r" 
or you won't be able to make the script work with all pdf file.

My final script (working for Open and Save on a 1.9 Mb complex PDF file) :

<?php
//The filename is stored in the $produitFilename variable in my script (the only thing you need)

// You need to specify the REAL path for your file and not the URL
$fullPath    getcwd()."./directory_where_the_file_is/".$produitFilename;

if (
$fd fopen ($fullPath"rb")) {
    
$fsize    =filesize($fullPath);
    
$fname     basename ($fullPath);

    
header("Pragma: ");
    
header("Cache-Control: ");
    
header("Content-type: application/octet-stream");
    
header("Content-Disposition: attachment; filename=\"".$fname."\"");
    
header("Content-length: $fsize");

    
fpassthru($fd);
}
?>


Have fun and thx u all 4 ur great help ...

Simon (from Paris - France)

[#17] Omega2k at web dot de [2003-03-27 05:07:51]

To throttle download-speed of specific files this works fine in my board hosted on my local machine:

//#######################################
 $big_file=filesize($completeFilePath)/1024; //size of file in kb
 header('Content-Type: '.$mime_type);
 header('Content-disposition: '.$content_disp.'filename="'.$attachment_name.'"');
 header('Cache-Control: no-cache');
 header('Pragma: no-cache');
 header('Expires: 0');
 header('Content-Length: '.(string)(filesize($completeFilePath))); 
 $fp=fopen($completeFilePath,'r');
 while(!feof($fp)) {
     $buffer = fread($fp, 1024*6); //speed-limit 6kb/s
     if ($big_file>32 && 
     $extension!="jpg" && 
     $extension!="jpeg" && 
     $extension!="gif" && 
     $extension!="png" && 
     $extension!="txt")
     sleep(1); //if filesize>32kb and no smallfile like jpg,gif or so - wait 1 second
     print $buffer;
 }
 fclose($fp);
 header ("Connection: close");
//#######################################

I think it's the easiest way to slow down downloading files without using a loop or for-next - this really saves performace of php and is quite exact by using 1024*number_of_kb in one second...

Thats all

Greetings, omega2k.dynu.com

[#18] brett at NOSPAM dot brettbrewer dot com [2003-02-04 14:49:40]

The above method worked for me after trying everything else imaginable to get Explorer to download a file via PHP. However, I had to change the content-length line. No need to "stringify" the $size variable as in the above post. The method below works for both small and very large file (tested on files larger than 30MB with no probs)...

<?php
$distribution
="/path/to/a/file.exe"
if ($fd fopen ($distribution"r")){

          
$size=filesize($distribution);
          
$fname basename ($distribution);

//This is some really weak code I used just to redirect to the file before I fixed
//this problem...it makes the browser handle the download via Apache instead of PHP
//but it would be really easy to then find out the true location of the file

               //header("Location: $distribution");
             //fclose ($fd);
             //exit;

//below is a much better way to do it...

     
header("Pragma: ");
      
header("Cache-Control: ");
     
header("Content-type: application/octet-stream");
      
header("Content-Disposition: attachment; filename=\"".$fname."\"");
      
header("Content-length: $size");

          while(!
feof($fd)) {
               
$buffer fread($fd2048);
               print 
$buffer;
          }
          
fclose ($fd);
          exit;
}
?>


Good luck.
Brett Brewer.

[#19] shaun at nospam dot phplabs dot com [2002-11-29 16:41:45]

Note that if you use these two headers from a previous example:

header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');

before sending a file to the browser, the "Open" option on Internet Explorer's file download dialog will not work properly. If the user clicks "Open" instead of "Save," the target application will open an empty file, because the downloaded file was not cached. The user will have to save the file to their hard drive in order to use it. 

Make sure to leave these headers out if you'd like your visitors to be able to use IE's "Open" option.

[#20] john at bvstudios dot com [2002-11-21 12:05:28]

In reply to:

"3. Through no amount of futzing of headers was I able to get the filename to be set properly when the actual transfer was initiated via a refresh (META or via headers).  I don't know if this is also an MSIE only issue or not.  If 'download.php?dl=now' (for example) had a refresh back to 'download.php', such that it was intended to show some information (e.g. install instructions) as well as launch the download, then the MSIE insisted that the downloaded file was supposed to be named 'download.php?dl=now' or 'download.php', ignoring the filename in the headers."

I recently had the exact same issue.  What I found is that this was due to my session initialization on the page.  For some reason doing a session_start() caused the script to try and download itself, not what I was indicating through various header() calls.

The solution was to move the download portion above the session initialization.  At first glance this may seem dangerous, but I only process it if there are POST vars and the script is reloading itself.  This way I know the form was submitted by that page and before they can submit it, they have to have a session!  Adding an .htaccess rule to deny all for the directory where the files are stored also helps because then only my script can access the files.

[#21] mikek at nospam dot muonics dot c o m [2002-11-05 02:07:32]

Found a workaround to another headache that just cropped up tonight.  Apparently Opera 6.1 on Linux (unsure of other versions/platforms) has problems downloading files using the above methods if you have enabled compression via zlib.output_compression in php.ini.

It seems that Opera sees that the actual transfer size is less than the size in the "Content-length" header for the download and decides that the transfer was incomplete or corrupted.  It then either continuously retries the download or else leaves you with a corrupted file.

Solution:  Make sure your download script/section is off in its own directory. and add the following to your .htaccess file for that directory:

php_flag zlib.output_compression off

[#22] claude_minette at hotmail dot com [2002-10-30 03:51:45]

This code works fine with a download manager... maybe not the best solution, but the only one that works with IE!!!!!

It forces download, but gif file don't want to be downloaded!!! so I need to simply display them in browser...

NB $file is the result of a query on the file table...

require_once("auth.inc.php");
$attachment = (strstr($HTTP_USER_AGENT, "MSIE")) ? "" : " attachment"; // IE 5.5 fix.
//Content of file
if (!headers_sent()){
   $ficexp=explode('.',$file["orig_name"]);
   $ext=$ficexp[sizeof($ficexp)-1];
   if ($ext!='gif'){
    header('Cache-Control: no-cache, must-revalidate');
    header('Pragma: no-cache');
    header("Content-Type: application/force-download");
    header("Content-Length: ".filesize("files/".$file["save_name"]));
    header("Content-Disposition: ".$attachment."; filename=".$file["orig_name"]);
   }
   $fn=fopen("files/".$file["save_name"], "rb");
   fpassthru($fn);
}
else {
MessageBox('Headers already sent, cannot force download!');
}

Min's

[#23] mikek at muonics dot nospam dot c dot o [2002-10-07 03:44:14]

Found a workaround for the MSIE cache bug that puts brackets around dotted items I posted about a while back (e.g. "somefile1.0-xyz.zip" becoming "somefile[1][0]-xyz.zip").

It turns out if you encode all but the last dot as %2e, then MSIE won't do this.  If you encode all of them (including the last dot), then MSIE sticks an extra bracketed number at the end of the file (e.g. "somefile1.0-xyz.zip[1]").  Unfortunately, however, some other browsers then want to save the file with the %2e in the filename instead of the dots.

if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
    $fileName = preg_replace('/\./', '%2e', $fileName,
        substr_count($fileName, '.') - 1);
}

Viola.  Properly named files.  This works at least with MSIE 6.0.

[#24] mikek at muonics dot nospam dot c dot o [2002-10-04 08:48:22]

If your downloaded files are getting corrupted, one of the scripts included/required in your download script or page may have whitespace around the  <?php ?#=#> tags.  A common enough problem, but most often recognized when header() fails, due to headers already being sent, but one worth mention here.

This one bit me just recently with my download script.  Somewhere along the way adding functionality to my websiteI wound up with a space (not a blank linewhich I usually spot right awaybut a single space characterafter the closing ?>
 tag in one of the require()'d files.  Oddly enough, all the downloads seemed to work ok, but the files were corrupted: that space character wound up at the beginning of each file.

[#25] [2002-09-10 05:17:46]

Note that the above comment about the "Connection: close" header is incorrect: it does not guarantee that the connection will be closed immediately after the transfer is complete. Instead, it informs the client that it can no longer use the existing HTTP connection to perform other HTTP requests on the same server, and that the client MUST close the connection as soon as it has finished handling the current request.

If the client (for example an old HTTP proxy) is using HTTP/1.0, it may not recognize this header, and could could the connection open; the web server should detect this and close the connection and ignore any further request attempt on that connection.

HTTP/1.1 clients MUST honor this header and close their connection as soon as they detect the end of the answer.

In any case, the web server will initiate a watchdog after script completion, and will force the deconnection after about 15 to 30 seconds if the client does not honor this header.

The exact time to wait for the "socket closed by remote" event is configurable in the web server.

It is generally smaller when the "Connection: close" header has been sent by the server, than when no "Connection:close" has been sent (in which case the connection persists for longer time, to let the client navigate on the server without enduring new connection costs in terms of: connection delays, number of socket control blocks in final wait state, number of used ports).

Don't abuse "Connection: close" on your server for every hosted page: this creates more incoming TCP connection attempts than necessary, and slows the navigation on your site. Use it only if your script cannot generate explicit content length in the result header, as the client will have difficulties to determine the end of the results.

If you want to save connection resources to your server, always send an Explicit "Content-Length" header within your script, or use the "chunked" transfer-encoding to explicitly send the result by delimited fragments (if the client is using HTTP/1.1, it MUST support this chunked transfer encoding, per specification). See RFC2616 for details.

[#26] [2002-09-10 04:38:04]

fpassthru() works best for small files. In download manager scripts, it's best to determine the URL of the file to download (you may generate it locally in your session data if you need so), and then use HTTP __temporary__ redirects (302 status code, with a "Location:" header specifying the effective download URL).

This saves your web server from maintaining PHP scripts running for long times during the file downloadn and instead the download will be managed directly by the web server without scripting support (consequence: less memory resources used by parallel downloads)...

[#27] me at gavinadams dot org [2002-08-29 19:06:05]

Interesting results using fpassthru() vs. fread() under UNIX.

Using fread(fp, length) to read from a valid, open pointer, in which the filename has a special character (single quote, comma, open paren, etc) fails on the read (no debug statements written after that). However, using fpassthru() works like a champ.

Thanks for the helpful notes on IE session info, have seen this before but didn't know what was causing it.

[#28] mikek at muonics dot nospam dot c dot o dot m [2002-08-24 09:14:12]

A few notes on using fpassthru() to php-driven download links that pop up a "Save As.." dialog:

1. I found that the download progress dialog was remaining up for several seconds after the transfer was completed, before telling the user it was complete.  This was fixed by adding the following header:

header ("Connection: close");

This will cause the connection to be closed as soon as the transfer is complete, rather than waiting for a timeout.

2. If you have multiple periods in the filename, you might wind up with a filename with numbers in brackets (such as myfile-[1][0]-windows.zip when you put myfile-1.0-windows.zip in the headers) with MSIE.  According to Microsoft's KB, his is a "known" bug having to due with MSIE's cache and there's no workaround that I was able to find.

3. Through no amount of futzing of headers was I able to get the filename to be set properly when the actual transfer was initiated via a refresh (META or via headers).  I don't know if this is also an MSIE only issue or not.  If 'download.php?dl=now' (for example) had a refresh back to 'download.php', such that it was intended to show some information (e.g. install instructions) as well as launch the download, then the MSIE insisted that the downloaded file was supposed to be named 'download.php?dl=now' or 'download.php', ignoring the filename in the headers.

[#29] - [2002-07-24 16:37:39]

If you trying to output a user-written file on a page for verifying, editing, etc, you'll want to use fopen(), fread(), htmlentities() to avoid malicious code. Text from fpassthru, while not parsed per se can still mess up the display of a page (or at least it did for me!) --mt.

[#30] josh at trutwins dot homeip dot net [2002-04-23 17:03:02]

I could not get the above examples to work.  This is what I used instead:

header("Content-Disposition: attachment; filename=$file");
header("Content-Description: Image File");
$fd = fopen($file,'r');
fpassthru($fd);

[#31] php at brayra dot com [2002-04-04 12:25:13]

Here is a final working copy that won't freak out Microsoft Explorer if you are using sessions. Thanks to everyone else who came before. This is not as simple as I thought it would be.

the user would pass a call to the page:
http://mysite/getfile.php?file=products.pdf

include 'base.inc'; // inlcude base code, start session  and manage users

// This loads the file global from the post/get variables
// For security reasons register globals is disabled
LoadPostGet('file');

$filename = '/data/files/' . $file;
if(file_exists($filename)){
  $FILECMD = '/usr/bin/file';
  $contentType = '';
  $fp=popen("$FILECMD -bin $filename", 'r');
  if (!$fp) $contentType='application/octet-stream';
  else {
    while($string=fgets($fp, 1024)) $contentType .= $string;
    pclose($fp);
  }
  if(strpos($HTTP_SERVER_VARS['HTTP_USER_AGENT'], 'MSIE')){
    // IE cannot download from sessions without a cache
    header('Cache-Control: public');
  }
  header("Content-type: $contentType");
  header("Content-Disposition:inline; filename=\"".$file."\"");
  header("Content-length:".(string)(filesize($filename)));
  $fd=fopen($filename,'rb');
  while(!feof($fd)) {
    print fread($fd, 4096);
  }
  fclose($fd);
}else{
  print "File Not Found";
}

[#32] mirko at mcaserta dot com [2002-01-23 05:07:14]

Update to the above. This also sets the correct mime type for the file you're sending. It's a small hack since it relies on the "file" system command but it should work well.

<?php
// full path to the file command
$FILECMD='/usr/bin/file';
// directory where the file resides
$fileDir='/home/mcaserta';
// full file name
$fileName='test.sh';

// END CONFIG

$completeFilePath=$fileDir.'/'.$fileName;
$fp=popen("$FILECMD -bin $completeFilePath"'r');

if (! 
$fp$contentType='application/octet-stream';
else {
  while(
$string=fgets($fp1024)) $contentType .= $string;
  
pclose($fp);
}

header('Content-type: '.($contentType));
header('Content-Disposition: inline; filename="'.($fileName).'"');
header('Content-length: '.(string)(filesize($completeFilePath)));
$fd=fopen($completeFilePath,'r');
fpassthru($fd); 
?>

[#33] straz at -removethispart-mac dot com [2002-01-15 00:22:51]

I wrote a page which authenticates the user, then calls fpassthru() to download an Acrobat document. It worked great up to about 1MB, but for larger files, the script was dying in the middle. My ISP told me they were killing my script because it was a memory hog. I tried readfile() instead, to no avail.

I replaced the fpassthru() with this workaround. It works great:

 while(!feof($fn)) {
   $buffer = fread($fn, 4096);
   print $buffer;
 }

[#34] cgriffin at websales dot com [1999-10-30 16:56:18]

If you open a new file, write to it and then call fpassthru() it doesn't work. You need to call rewind() first to set the file pointer to the begining of the file.

上一篇: 下一篇: