编程语言您现在的位置是:首页 > 博客日志 > 编程语言

PHP下载远程图片的几种方法总结,区别比较

<a href='mailto:'>微wx笑</a>的头像微wx笑 2019-09-04编程语言 26 0关键字: php  

PHP下载远程图片有多种方法,包括:file_get_contents、CURL、fopen、readfile等,这些方法应该怎么使用?有什么区别?我该怎么选择呢?

PHP下载远程图片的几种方法总结xJf无知

1、使用file_get_contents

function dlfile($file_url, $save_to)
{
 $content = file_get_contents($file_url);
 file_put_contents($save_to, $content);
}

定义和用法

file_get_contents() 把整个文件读入一个字符串中。xJf无知

该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。xJf无知

语法

file_get_contents(path,include_path,context,start,max_length)


xJf无知

参数描述
path必需。规定要读取的文件。
include_path可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。
context可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 NULL,则忽略。
start可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 中新增的。
max_length可选。规定读取的字节数。该参数是 PHP 5.1 中新增的。


xJf无知


提示和注释

提示:该函数是二进制安全的。(意思是二进制数据(如图像)和字符数据都可以使用此函数写入。)xJf无知


xJf无知

2、使用CURL

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库允许你与各种的服务器使用各种类型的协议进行连接和通讯。xJf无知

libcurl目前支持http、https、ftp、gopher、telnet、dict、file和ldap协议。libcurl同时也支持HTTPS认证、HTTP POST、HTTP PUT、 FTP 上传(这个也能通过PHP的FTP扩展完成)、HTTP 基于表单的上传、代理、cookies和用户名+密码的认证。xJf无知

PHP中使用cURL实现Get和Post请求的方法xJf无知

这些函数在PHP 4.0.2中被引入。xJf无知

CURL的相关内容非常多,这里就不展开了。xJf无知

可以参考这里:https://www.runoob.com/php/php-ref-curl.html xJf无知


xJf无知

3、使用fopen

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。xJf无知

如果 fopen() 失败,它将返回 FALSE 并附带错误信息。您可以通过在函数名前面添加一个 '@' 来隐藏错误输出。xJf无知

语法

fopen(filename,mode,include_path,context)


xJf无知

参数描述
filename必需。规定要打开的文件或 URL。
mode必需。规定您请求到该文件/流的访问类型。

可能的值:xJf无知

  • "r" (只读方式打开,将文件指针指向文件头)xJf无知

  • "r+" (读写方式打开,将文件指针指向文件头)xJf无知

  • "w" (写入方式打开,清除文件内容,如果文件不存在则尝试创建之)xJf无知

  • "w+" (读写方式打开,清除文件内容,如果文件不存在则尝试创建之)xJf无知

  • "a" (写入方式打开,将文件指针指向文件末尾进行写入,如果文件不存在则尝试创建之)xJf无知

  • "a+" (读写方式打开,通过将文件指针指向文件末尾进行写入来保存文件内容)xJf无知

  • "x" (创建一个新的文件并以写入方式打开,如果文件已存在则返回 FALSE 和一个错误)xJf无知

  • "x+" (创建一个新的文件并以读写方式打开,如果文件已存在则返回 FALSE 和一个错误)xJf无知

include_path可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。
context可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。


xJf无知


提示和注释

注释:当书写一个文本文件时,请确保您使用了正确的行结束符!在 Unix 系统中,行结束符为 \n;在 Windows 系统中,行结束符为 \r\n;在 Macintosh 系统中,行结束符为 \r。Windows 系统中提供了一个文本转换标记 "t" ,可以透明地将 \n 转换为 \r\n。您还可以使用 "b" 来强制使用二进制模式,这样就不会转换数据。为了使用这些标记,请使用 "b" 或者 "t" 来作为 mode 参数的最后一个字符。xJf无知


xJf无知

4、使用readfile

参考:xJf无知

    /**
     * 拉取远程图片
     * @return mixed
     */
    private function saveRemote()
    {
        $imgUrl = htmlspecialchars($this->fileField);
        $imgUrl = str_replace("&amp;", "&", $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() 函数读取一个文件,并写入到输出缓冲。xJf无知

如果成功,该函数返回从文件中读入的字节数。如果失败,该函数返回 FALSE 并附带错误信息。您可以通过在函数名前面添加一个 '@' 来隐藏错误输出。xJf无知

语法

readfile(filename,include_path,context)


xJf无知

参数描述
filename必需。规定要读取的文件。
include_path可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'。
context可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。


xJf无知


提示和注释

提示:如果在 php.ini 文件中 "fopen wrappers" 已经被激活,您可以在本函数中把 URL 作为文件名来使用。xJf无知

5、fsockopen

fsockopen以Socket方式模拟HTTP下载文件xJf无知

fsockopen 的功能很强大,比如模拟 HTTP 访问,模拟 POST/GET 请求什么的,这里再举一个例子,那就是下载东西。比如下载 http://www.nowamagic.net//librarys/webapp/Snow.zip 这个文件,用下面的程序就能实现:xJf无知

# 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);
}

执行程序,你会发现在这个程序文件的同级目录就会出现那个你需要下载的文件了。xJf无知


xJf无知

这实质上是 Socket 模拟 HTTP 协议传输文件。同时还要注意一下 PHP 的超时限制,这里设置我 PHP 服务器超时为无限才能正确下载,否则可能下载不全 PHP 程序就停止了。xJf无知


xJf无知

同时,set_socket_blocking 和 stream_set_blocking 的用法也要稍微注意下,Google一下就能了解,这里不再赘述。xJf无知

下载与保存完整方法:

    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长度的字节数,所以不能一次性读取大文件去作下载。 优势在于,操作更加灵活,每次读取指定字节的内容,用于下载时方便控制服务器的流量。xJf无知

readfile —— 优势是能够一次性读取大文件;不需要PHP预读到内存,下载速度更快,直接把文件的处理交由服务器。缺点就是不能控制负载。所以它是没有内存限制的,如果遇到报内存错误,先调用 ob_end_flush()之类的函数关闭缓冲区。xJf无知

file_get_contents —— 将整个文件读入一个字符串,也是没 readfile()快, 因为也是走了php的内存。但是在读取小文本内容到字符串变量时,这个函数最适合使用,简单,更快。xJf无知

fgets —— 一次读取文件中的一行,在需要逐行处理文件的时候,使用这个。注意:fgets不支持打开url。xJf无知

本文由 微wx笑 创作,采用 署名-非商业性使用-相同方式共享 4.0 许可协议,转载请附上原文出处链接及本声明。
原文链接:https://www.ivu4e.com/blog/lang/2019-09-04/180.html

很赞哦! () 有话说 ()