为什么用fopenfopen 打开远程文件URL会很慢

2012年6月 Web 开发大版内专家分月排行榜第二2012年5月 Web 开发大版内专家分月排行榜第二2011年5月 PHP大版内专家分月排行榜第二2008年9月 硬件使用大版内专家分月排行榜第二
2012年7月 Web 开发大版内专家分月排行榜第三2011年8月 PHP大版内专家分月排行榜第三2011年8月 Web 开发大版内专家分月排行榜第三2011年7月 PHP大版内专家分月排行榜第三2011年4月 Web 开发大版内专家分月排行榜第三2008年9月 Windows专区大版内专家分月排行榜第三
2012年6月 Web 开发大版内专家分月排行榜第二2012年5月 Web 开发大版内专家分月排行榜第二2011年5月 PHP大版内专家分月排行榜第二2008年9月 硬件使用大版内专家分月排行榜第二
2012年7月 Web 开发大版内专家分月排行榜第三2011年8月 PHP大版内专家分月排行榜第三2011年8月 Web 开发大版内专家分月排行榜第三2011年7月 PHP大版内专家分月排行榜第三2011年4月 Web 开发大版内专家分月排行榜第三2008年9月 Windows专区大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。使用远程文件
使用远程文件
只要在 php.ini 文件中激活了 allow_url_fopen
选项,就可以在大多数需要用文件名作为参数的函数中使用
HTTP 和 FTP 的 URL
来代替文件名。同时,也可以在
语句中使用 URL。PHP
所支持协议的更多信息参见。
要在 PHP 4.0.3 及其更早的版本中使用 URL 封装协议,需要在编译时用
--enable-url-fopen-wrapper 参数来配置 PHP。
Windows 版本的 PHP 4.3
版之前不支持以下函数的远程访问:,,,
以及中的 imagecreatefromXXX 函数。
例如,可以用以下范例来打开远程 web
服务器上的文件,解析需要的输出数据,然后将这些数据用在数据库的检索中,或者简单地以和自己网站其它页面相同的风格输出其内容。
Example#1 获取远程文件的标题
&?php$file&=&fopen&("/",&"r");if&(!$file)&{&&&&echo&"&p&Unable&to&open&remote&file.\n";&&&&}while&(!feof&($file))&{&&&&$line&=&fgets&($file,&1024);&&&&/*&This&only&works&if&the&title&and&its&tags&are&on&one&line&*/&&&&if&(eregi&("&title&(.*)&/title&",&$line,&$out))&{&&&&&&&&$title&=&$out[1];&&&&&&&&&&&&}}fclose($file);?&
如果有合法的访问权限,以一个用户的身份和某 FTP
服务器建立了链接,还可以向该 FTP
服务器端的文件进行写操作。仅能用该方法来创建新的文件;如果尝试覆盖已经存在的文件,
函数的调用将会失败。
要以“anonymous”以外的用户名连接服务器,需要指明用户名(可能还有密码),例如“ftp://user:/path/to/file”(也可以在通过需要
Basic 认证的 HTTP 协议访问远程文件时使用相同的语法)。
Example#2 将数据保存到远程服务器
&?php$file&=&fopen&("/incoming/outputfile",&"w");if&(!$file)&{&&&&echo&"&p&Unable&to&open&remote&file&for&writing.\n";&&&&}/*&Write&the&data&here.&*/fwrite&($file,&$_SERVER['HTTP_USER_AGENT']&.&"\n");fclose&($file);?&
或许可以从以上范例中得到启发,用该技术来存储远程日志文件。但是正如以上提到的,在用
方式打开的 URL
中,仅能对新文件进行写操作。如果远程文件已经存在则
函数的操作将会失败。要做类似于分布式日志的事,可以参考为什么用fopen打开远程URL会很慢?
为什么用fopen打开远程URL会很慢?
1. HTTP HEADER之诡
$content = file_get_contents('/logo.png');
这是php中一种读取网络图片的方式,就跟读本地文件一样,用起来十分方便。但是这种方式存在一个问题,部分网络地址读起来速度非常快,但总有少数网址,读起来要卡顿10秒、20秒甚至1分钟。网上对于这个问题有很多的讨论,但答案基本都有问题。
对于慢,最常见的解释是DNS查询慢。的确,通过curl和wget的对比可以发现,获取同一个文件,curl需要0.5s,而wget需要1.5s,从wget的屏幕输出上看,其中1秒钟被他花在了DNS查询上。虽然DNS查询会占据1s的时间,但也不应该卡顿10秒呀。其中必有蹊跷。
国外有个同学终于给出了一个靠谱的答案:
By analyzing it with Wireshark, the issue (in my case and probably
yours too) was that the remote web server DIDN'T CLOSE THE TCP
CONNECTION UNTIL 15 SECONDS (i.e. &keep-alive&).
Indeed, file_get_contents doesn't send a &connection& HTTP header, so
the remote web server considers by default that's it's a keep-alive
connection and doesn't close the TCP stream until 15 seconds (It might
not be a standard value - depends on the server conf).
A normal browser would consider the page is fully loaded if the HTTP
payload length reaches the length specified in the response
Content-Length HTTP header. File_get_contents doesn't do this and
that's a shame.
问题出在file_get_contents的行为上:(1)它发起http连接的时候,没有带http头,(2)它接收完所有数据后,没有主动断开和服务器的http连接。
找到问题原因之后,解决方案就相对简单了:让file_get_contents也带上一个http的头,并且在头里面标注:Connection: 要求服务器发完数据后立即断开连接。
file_get_contents原型如下:
string file_get_contents (
string $filename [,
bool $use_include_path = false
[, resource $context
[, int $offset = -1
[, int $maxlen ]]]] )
其中$context参数就是用来传递http头的。代码如下:
$opts = array(
'http'=&array(
'method'=&&GET&,
'Connection'=&&close\r\n&
$context = stream_context_create($opts);
file_get_contents($filename, false, $context);
添加上$context后,性能立即得到了提升,每次读请求只需要1~2秒了。
注1:file_get_contents实际上是对fopen、fread、fclose的一个封装,所以它存在的问题,在fopen上同样存在。对于fopen来说,解决方案也是相同的。
2. DNS之谜
经过添加$option,性能已经得到了很大的提升。这时候DNS查询带来的开销就不可忽视了。如何消除掉这1秒的开销?目前我还没有基于fopen、file_get_contents的方案,只能用curl方案了。curl方案既不存在上面的HTTP
HEADER问题,也不存在DNS问题,是个非常不错的方案。
如果你选择用curl方案,同时还希望用上stream接口来读数据,可以了解一下curl的CUROPT_FILE这个option。
curl_setopt($ch, CURLOPT_FILE, $fp);
本文提出了解决网络读取图片慢的两种方案,对于移动开发、爬虫开发,微信开发,支付宝开发等各领域的朋友都有借鉴意义。
本文还让我重新思考了一个方法论:当网上搜不到答案时,外国友人用Wireshark监听数据包的方式其实是最直接合理高效的。为什么我没有这么做?想想,还是以前总结出来的工具论,我对这些工具的使用不熟练,不会往这个方向上想,就算想了,也不愿意行动。应该多动手,多学习!
我的热门文章
即使是一小步也想与你分享使用远程文件
只要在 php.ini 文件中激活了 allow_url_fopen 选项,您可以在大多数需要用文件名作为参数的函数中使用 HTTP 和 FTP URL 来代替文件名。同时,您也可以在 、、 及
语句中使用 URL。PHP
所支持协议的更多信息参见 。
要在 PHP 4.0.3 及其以后版本中使用 URL,您需要用 --enable-url-fopen-wrapper 参数来配置 PHP。
Windows 版本的 PHP 4.3
版之前不支持以下函数的远程访问:,,,
扩展库中的 imagecreatefromXXX 函数。
例如,您可以用以下范例来打开远程 WEB 服务器上的文件,解析您需要的输出数据,然后将这些数据用在数据库的检索中,或者简单地将其输出到您网站剩下内容的样式匹配中。
例子 19-1. 获取远程文件的标题
&?php$file = fopen ("/", "r");if (!$file) {&&&&echo "&p&Unable to open remote file.\n";&&&&}while (!feof ($file)) {&&&&$line = fgets ($file, 1024);&&&&/* This only works if the title and its tags are on one line */&&&&if (eregi ("&title&(.*)&/title&", $line, $out)) {&&&&&&&&$title = $out[1];&&&&&&&&&&&&}}fclose($file);?&
如果您用有合法的访问权限,以一个用户的身份和某 FTP 服务器建立了链接,您还可以向该 FTP 服务器端的文件进行写操作。您仅能用该方法来创建新的文件;如果您尝试覆盖已经存在的文件, 函数的调用将会失败。
要以“anonymous”以外的用户名连接服务器,您需要指明用户名(甚至密码),例如“ftp://user:password@/path/to/file”。(如果通过 HTTP 协议访问远程文件时需要基本身份认证,您也可以用使用的语法。)
例子 19-2. 远程服务端的数据存储
&?php$file = fopen ("/incoming/outputfile", "w");if (!$file) {&&&&echo "&p&Unable to open remote file for writing.\n";&&&&}/* Write the data here. */fputs ($file, $_SERVER['HTTP_USER_AGENT'] . "\n");fclose ($file);?&
您或许可以从以上范例中得到启发,用该技术来存储远程日志文件。但是正如以上提到的,在用 fopen() 方式打开的 URL 中,您仅能对新文件进行写操作。如果远程文件已经存在
函数的操作将会失败。要进行类似的分布式日志操作,您可以参考}

我要回帖

更多关于 fopen 远程文件 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信