PHP下载远程图片的几种方法总结,区别比较
微wx笑
2019-09-04【编程语言】
1116
27
0关键字:
php
PHP下载远程图片有多种方法,包括:file_get_contents、CURL、fopen、readfile等,这些方法应该怎么使用?有什么区别?我该怎么选择呢?
目录
PHP下载远程图片的几种方法总结
1、使用file_get_contents
1 2 3 4 5 | function dlfile( $file_url , $save_to ) { $content = file_get_contents ( $file_url ); file_put_contents ( $save_to , $content ); } |
定义和用法
file_get_contents() 把整个文件读入一个字符串中。
该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。
语法
参数 | 描述 |
---|---|
path | 必需。规定要读取的文件。 |
include_path | 可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。 |
context | 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 NULL,则忽略。 |
start | 可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 中新增的。 |
max_length | 可选。规定读取的字节数。该参数是 PHP 5.1 中新增的。 |
提示和注释
提示:该函数是二进制安全的。(意思是二进制数据(如图像)和字符数据都可以使用此函数写入。)
2、使用CURL
1 2 3 4 5 6 7 8 9 10 11 12 | function dlfile( $file_url , $save_to ) { $ch = curl_init(); curl_setopt( $ch , CURLOPT_POST, 0); curl_setopt( $ch ,CURLOPT_URL, $file_url ); curl_setopt( $ch , CURLOPT_RETURNTRANSFER, 1); $file_content = curl_exec( $ch ); curl_close( $ch ); $downloaded_file = fopen ( $save_to , 'w' ); fwrite( $downloaded_file , $file_content ); fclose( $downloaded_file ); } |
PHP支持的由Daniel Stenberg创建的libcurl库允许你与各种的服务器使用各种类型的协议进行连接和通讯。
libcurl目前支持http、https、ftp、gopher、telnet、dict、file和ldap协议。libcurl同时也支持HTTPS认证、HTTP POST、HTTP PUT、 FTP 上传(这个也能通过PHP的FTP扩展完成)、HTTP 基于表单的上传、代理、cookies和用户名+密码的认证。
PHP中使用cURL实现Get和Post请求的方法
这些函数在PHP 4.0.2中被引入。
CURL的相关内容非常多,这里就不展开了。
可以参考这里:https://www.runoob.com/php/php-ref-curl.html
3、使用fopen
1 2 3 4 5 6 7 8 9 10 11 | function dlfile( $file_url , $save_to ) { $in = fopen ( $file_url , "rb" ); $out = fopen ( $save_to , "wb" ); while ( $chunk = fread ( $in ,8192)) { fwrite( $out , $chunk , 8192); } fclose( $in ); fclose( $out ); } |
定义和用法
fopen() 函数打开一个文件或 URL。
如果 fopen() 失败,它将返回 FALSE 并附带错误信息。您可以通过在函数名前面添加一个 '@' 来隐藏错误输出。
语法
参数 | 描述 |
---|---|
filename | 必需。规定要打开的文件或 URL。 |
mode | 必需。规定您请求到该文件/流的访问类型。 可能的值:
|
include_path | 可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。 |
context | 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。 |
提示和注释
注释:当书写一个文本文件时,请确保您使用了正确的行结束符!在 Unix 系统中,行结束符为 \n;在 Windows 系统中,行结束符为 \r\n;在 Macintosh 系统中,行结束符为 \r。Windows 系统中提供了一个文本转换标记 "t" ,可以透明地将 \n 转换为 \r\n。您还可以使用 "b" 来强制使用二进制模式,这样就不会转换数据。为了使用这些标记,请使用 "b" 或者 "t" 来作为 mode 参数的最后一个字符。
4、使用readfile
参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | /** * 拉取远程图片 * @return mixed */ private function saveRemote() { $imgUrl = htmlspecialchars( $this ->fileField); $imgUrl = str_replace ( "&" , "&" , $imgUrl ); //http开头验证 if ( strpos ( $imgUrl , "http" ) !== 0) { $this ->stateInfo = $this ->getStateInfo( "ERROR_HTTP_LINK" ); return ; } preg_match( '/(^https*:\/\/[^:\/]+)/' , $imgUrl , $matches ); $host_with_protocol = count ( $matches ) > 1 ? $matches [1] : '' ; // 判断是否是合法 url if (!filter_var( $host_with_protocol , FILTER_VALIDATE_URL)) { $this ->stateInfo = $this ->getStateInfo( "INVALID_URL" ); return ; } preg_match( '/^https*:\/\/(.+)/' , $host_with_protocol , $matches ); $host_without_protocol = count ( $matches ) > 1 ? $matches [1] : '' ; // 此时提取出来的可能是 ip 也有可能是域名,先获取 ip $ip = gethostbyname ( $host_without_protocol ); // 判断是否是私有 ip if (!filter_var( $ip , FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) { $this ->stateInfo = $this ->getStateInfo( "INVALID_IP" ); return ; } //获取请求头并检测死链 $heads = get_headers( $imgUrl , 1); if (!( stristr ( $heads [0], "200" ) && stristr ( $heads [0], "OK" ))) { $this ->stateInfo = $this ->getStateInfo( "ERROR_DEAD_LINK" ); return ; } //格式验证(扩展名验证和Content-Type验证) $fileType = strtolower ( strrchr ( $imgUrl , '.' )); if ( strpos ( $fileType , "?" )){ $fileType = strstr ( $fileType , "?" , true); } if (!isset( $heads [ 'Content-Type' ]) || ! stristr ( $heads [ 'Content-Type' ], "image" )) { $this ->stateInfo = $this ->getStateInfo( "ERROR_HTTP_CONTENTTYPE" ); return ; } else { if ( count ( $this ->config[ 'allowFiles' ]) > 0){ if (!in_array( $fileType , $this ->config[ 'allowFiles' ])){ //$this->stateInfo = $this->getStateInfo("ERROR_HTTP_ALLOWFILES").$heads['Content-Type']; //image/webp return ; } } } //打开输出缓冲区并获取远程图片 ob_start(); $context = stream_context_create( array ( 'http' => array ( 'follow_location' => false // don't follow redirects )) ); readfile( $imgUrl , false, $context ); $img = ob_get_contents(); ob_end_clean(); $imgUrl2 = $imgUrl ; if ( strpos ( $imgUrl , "?" )){ $imgUrl2 = substr ( $imgUrl , 0, strripos ( $imgUrl , "?" )); } preg_match( "/[\/]([^\/]*)[\.]?[^\.\/]*$/" , $imgUrl2 , $m ); $this ->oriName = $m ? $m [1]: "" ; $this -> fileSize = strlen ( $img ); $this -> fileType = $this ->getFileExt(); $this ->fullName = $this ->getFullName(); $this ->filePath = $this ->getFilePath(); $this ->fileName = $this ->getFileName(); $dirname = dirname( $this ->filePath); //检查文件大小是否超出限制 if (! $this ->checkSize()) { $this ->stateInfo = $this ->getStateInfo( "ERROR_SIZE_EXCEED" ); return ; } //创建目录失败 if (! file_exists ( $dirname ) && ! mkdir ( $dirname , 0777, true)) { $this ->stateInfo = $this ->getStateInfo( "ERROR_CREATE_DIR" ); return ; } else if (! is_writeable ( $dirname )) { $this ->stateInfo = $this ->getStateInfo( "ERROR_DIR_NOT_WRITEABLE" ); return ; } //移动文件 if (!( file_put_contents ( $this ->filePath, $img ) && file_exists ( $this ->filePath))) { //移动失败 $this ->stateInfo = $this ->getStateInfo( "ERROR_WRITE_CONTENT" ); } else { //移动成功 $this ->stateInfo = $this ->stateMap[0]; } } |
定义和用法
readfile() 函数读取一个文件,并写入到输出缓冲。
如果成功,该函数返回从文件中读入的字节数。如果失败,该函数返回 FALSE 并附带错误信息。您可以通过在函数名前面添加一个 '@' 来隐藏错误输出。
语法
参数 | 描述 |
---|---|
filename | 必需。规定要读取的文件。 |
include_path | 可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。 |
context | 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。 |
提示和注释
提示:如果在 php.ini 文件中 "fopen wrappers" 已经被激活,您可以在本函数中把 URL 作为文件名来使用。
5、fsockopen
fsockopen以Socket方式模拟HTTP下载文件
fsockopen 的功能很强大,比如模拟 HTTP 访问,模拟 POST/GET 请求什么的,这里再举一个例子,那就是下载东西。比如下载 http://www.nowamagic.net//librarys/webapp/Snow.zip 这个文件,用下面的程序就能实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # Socket 模拟HTTP协议传输文件 # Http是应用层协议使用端口80 # $hostname = 'www.nowamagic.net' ; $port = '80' ; # 建立连接 $fp = fsockopen ( $hostname , $port , $errno , $errstr ); //set_socket_blocking($fp,false); //stream_set_blocking($fp,0); stream_set_blocking( $fp , true); if (! $fp ) { echo "$errno : $errstr<br/>" ; } else { # 发送一个HTTP请求信息头 $request_header = "GET /librarys/webapp/Snow.zip HTTP/1.1\n" ; # 起始行 # 头域 $request_header .= "Host: $hostname\n" ; # 再一个回车换行表示头信息结束 $request_header .= "\n" ; # 发送请求到服务器 fputs ( $fp , $request_header ); # 接受响应 $fp2 = fopen ( 'Snow.zip' , 'w' ); while (! feof ( $fp )) { $line = fputs ( $fp2 , fgets ( $fp ,128)); //echo $line; } # 关闭 fclose( $fp2 ); fclose( $fp ); } |
执行程序,你会发现在这个程序文件的同级目录就会出现那个你需要下载的文件了。
这实质上是 Socket 模拟 HTTP 协议传输文件。同时还要注意一下 PHP 的超时限制,这里设置我 PHP 服务器超时为无限才能正确下载,否则可能下载不全 PHP 程序就停止了。
同时,set_socket_blocking 和 stream_set_blocking 的用法也要稍微注意下,Google一下就能了解,这里不再赘述。
下载与保存完整方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private function downloadImage( $url ) { $ch = curl_init(); curl_setopt( $ch , CURLOPT_URL, $url ); curl_setopt( $ch , CURLOPT_RETURNTRANSFER, 1); curl_setopt( $ch , CURLOPT_CONNECTTIMEOUT, 30); $file = curl_exec( $ch ); curl_close( $ch ); $this ->saveAsImage( $url , $file ); } private function saveAsImage( $url , $file ) { $filename = pathinfo ( $url , PATHINFO_BASENAME); $dirname = pathinfo ( parse_url ( $url , PHP_URL_PATH), PATHINFO_DIRNAME); $path = 'public' . $dirname . '/' ; $fullpath = 'public' . $dirname . '/' . $filename ; // 如果目录不存在,则创建 if (! is_dir ( $path )) { mkdir ( $path , 0777, true); } if ( file_exists ( $fullpath )) { //$this->output->writeln("【已存在】输出路径" . $fullpath); } else { $resource = fopen ( $fullpath , 'a' ); fwrite( $resource , $file ); fclose( $resource ); //$this->output->writeln("【已保存】输出路径" . $fullpath); } } |
区别比较
fread —— 最大一次性能读取 8k长度的字节数,所以不能一次性读取大文件去作下载。 优势在于,操作更加灵活,每次读取指定字节的内容,用于下载时方便控制服务器的流量。
readfile —— 优势是能够一次性读取大文件;不需要PHP预读到内存,下载速度更快,直接把文件的处理交由服务器。缺点就是不能控制负载。所以它是没有内存限制的,如果遇到报内存错误,先调用 ob_end_flush()之类的函数关闭缓冲区。
file_get_contents —— 将整个文件读入一个字符串,也是没 readfile()快, 因为也是走了php的内存。但是在读取小文本内容到字符串变量时,这个函数最适合使用,简单,更快。
fgets —— 一次读取文件中的一行,在需要逐行处理文件的时候,使用这个。注意:fgets不支持打开url。
本文由 微wx笑 创作,采用 署名-非商业性使用-相同方式共享 4.0 许可协议,转载请附上原文出处链接及本声明。
原文链接:https://www.ivu4e.cn/blog/lang/2019-09-04/180.html