由于工作中有一个下载远程图片的需求,所以在网上搜索了一下相关方法, 发现网上大多数代码类似如下:
/*
*
* 抓取远程图片
*
* @param string $url 远程图片路径
* @param string $filename 本地存储文件名
*/
function grabImage( $url, $filename = '') {
if( $url == '') {
return false; // 如果 $url 为空则返回 false;
}
$ext_name = strrchr( $url, '.'); // 获取图片的扩展名
if( $ext_name != '.gif' && $ext_name != '.jpg' && $ext_name != '.bmp' && $ext_name != '.png') {
return false; // 格式不在允许的范围
}
if( $filename == '') {
$filename = time(). $ext_name; // 以时间戳另起名
}
// 开始捕获
ob_start();
readfile( $url);
$img_data = ob_get_contents();
ob_end_clean();
$size = strlen( $img_data);
$local_file = fopen( $filename , 'a');
fwrite( $local_file, $img_data);
fclose( $local_file);
return $filename;
}
* 抓取远程图片
*
* @param string $url 远程图片路径
* @param string $filename 本地存储文件名
*/
function grabImage( $url, $filename = '') {
if( $url == '') {
return false; // 如果 $url 为空则返回 false;
}
$ext_name = strrchr( $url, '.'); // 获取图片的扩展名
if( $ext_name != '.gif' && $ext_name != '.jpg' && $ext_name != '.bmp' && $ext_name != '.png') {
return false; // 格式不在允许的范围
}
if( $filename == '') {
$filename = time(). $ext_name; // 以时间戳另起名
}
// 开始捕获
ob_start();
readfile( $url);
$img_data = ob_get_contents();
ob_end_clean();
$size = strlen( $img_data);
$local_file = fopen( $filename , 'a');
fwrite( $local_file, $img_data);
fclose( $local_file);
return $filename;
}
上面的函数有几个缺点:
1.不能自动识别图片后缀名(很多图片的url并不指向一个静态图片地址,而是直接将图片流输出到客户端)
2.不支持图片url的302跳转
这个函数并不符合本人项目的需求,于是花了点时间自己写了一个下载函数,此函数支持:
1.静态图片下载
2.服务端直接输出图片流下载
3.服务端使用302跳转到真实图片地址的下载(可限定跳转次数)
函数代码如下:
/*
*
* 下载远程图片
* @param string $url 图片的绝对url
* @param string $filepath 文件的完整路径(包括目录,不包括后缀名,例如/www/images/test) ,此函数会自动根据图片url和http头信息确定图片的后缀名
* @return mixed 下载成功返回一个描述图片信息的数组,下载失败则返回false
*/
function downloadImage( $url, $filepath) {
// 服务器返回的头信息
$responseHeaders = array();
// 原始图片名
$originalfilename = '';
// 图片的后缀名
$ext = '';
$ch = curl_init( $url);
// 设置curl_exec返回的值包含Http头
curl_setopt( $ch, CURLOPT_HEADER, 1);
// 设置curl_exec返回的值包含Http内容
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
//设置抓取跳转(http 301,302)后的页面
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
// 设置最多的HTTP重定向的数量
curl_setopt( $ch, CURLOPT_MAXREDIRS, 2);
// 服务器返回的数据(包括http头信息和内容)
$html = curl_exec( $ch);
// 获取此次抓取的相关信息
$httpinfo = curl_getinfo( $ch);
curl_close( $ch);
if ( $html !== false) {
// 分离response的header和body,由于服务器可能使用了302跳转,所以此处需要将字符串分离为 2+跳转次数 个子串
$httpArr = explode("\r\n\r\n", $html, 2 + $httpinfo['redirect_count']);
// 倒数第二段是服务器最后一次response的http头
$header = $httpArr[ count( $httpArr) - 2];
// 倒数第一段是服务器最后一次response的内容
$body = $httpArr[ count( $httpArr) - 1];
$header.="\r\n";
// 获取最后一次response的header信息
preg_match_all('/([a-z0-9-_]+):\s*([^\r\n]+)\r\n/i', $header, $matches);
if (! empty( $matches) && count( $matches) == 3 && ! empty( $matches[1]) && ! empty( $matches[1])) {
for ( $i = 0; $i < count( $matches[1]); $i++) {
if ( array_key_exists( $i, $matches[2])) {
$responseHeaders[ $matches[1][ $i]] = $matches[2][ $i];
}
}
}
// 获取图片后缀名
if (0 < preg_match('{(?:[^\/\\\\]+)\.(jpg|jpeg|gif|png|bmp)$}i', $url, $matches)) {
$originalfilename = $matches[0];
$ext = $matches[1];
} else {
if ( array_key_exists('Content-Type', $responseHeaders)) {
if (0 < preg_match('{image/(\w+)}i', $responseHeaders['Content-Type'], $extmatches)) {
$ext = $extmatches[1];
}
}
}
// 保存文件
if (! empty( $ext)) {
$filepath .= ". $ext";
// 如果目录不存在,则先要创建目录
CFiles::createDirectory( dirname( $filepath));
$local_file = fopen( $filepath, 'w');
if ( false !== $local_file) {
if ( false !== fwrite( $local_file, $body)) {
fclose( $local_file);
$sizeinfo = getimagesize( $filepath);
return array('filepath' => realpath( $filepath), 'width' => $sizeinfo[0], 'height' => $sizeinfo[1], 'orginalfilename' => $originalfilename, 'filename' => pathinfo( $filepath, PATHINFO_BASENAME));
}
}
}
}
return false;
}
* 下载远程图片
* @param string $url 图片的绝对url
* @param string $filepath 文件的完整路径(包括目录,不包括后缀名,例如/www/images/test) ,此函数会自动根据图片url和http头信息确定图片的后缀名
* @return mixed 下载成功返回一个描述图片信息的数组,下载失败则返回false
*/
function downloadImage( $url, $filepath) {
// 服务器返回的头信息
$responseHeaders = array();
// 原始图片名
$originalfilename = '';
// 图片的后缀名
$ext = '';
$ch = curl_init( $url);
// 设置curl_exec返回的值包含Http头
curl_setopt( $ch, CURLOPT_HEADER, 1);
// 设置curl_exec返回的值包含Http内容
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
//设置抓取跳转(http 301,302)后的页面
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
// 设置最多的HTTP重定向的数量
curl_setopt( $ch, CURLOPT_MAXREDIRS, 2);
// 服务器返回的数据(包括http头信息和内容)
$html = curl_exec( $ch);
// 获取此次抓取的相关信息
$httpinfo = curl_getinfo( $ch);
curl_close( $ch);
if ( $html !== false) {
// 分离response的header和body,由于服务器可能使用了302跳转,所以此处需要将字符串分离为 2+跳转次数 个子串
$httpArr = explode("\r\n\r\n", $html, 2 + $httpinfo['redirect_count']);
// 倒数第二段是服务器最后一次response的http头
$header = $httpArr[ count( $httpArr) - 2];
// 倒数第一段是服务器最后一次response的内容
$body = $httpArr[ count( $httpArr) - 1];
$header.="\r\n";
// 获取最后一次response的header信息
preg_match_all('/([a-z0-9-_]+):\s*([^\r\n]+)\r\n/i', $header, $matches);
if (! empty( $matches) && count( $matches) == 3 && ! empty( $matches[1]) && ! empty( $matches[1])) {
for ( $i = 0; $i < count( $matches[1]); $i++) {
if ( array_key_exists( $i, $matches[2])) {
$responseHeaders[ $matches[1][ $i]] = $matches[2][ $i];
}
}
}
// 获取图片后缀名
if (0 < preg_match('{(?:[^\/\\\\]+)\.(jpg|jpeg|gif|png|bmp)$}i', $url, $matches)) {
$originalfilename = $matches[0];
$ext = $matches[1];
} else {
if ( array_key_exists('Content-Type', $responseHeaders)) {
if (0 < preg_match('{image/(\w+)}i', $responseHeaders['Content-Type'], $extmatches)) {
$ext = $extmatches[1];
}
}
}
// 保存文件
if (! empty( $ext)) {
$filepath .= ". $ext";
// 如果目录不存在,则先要创建目录
CFiles::createDirectory( dirname( $filepath));
$local_file = fopen( $filepath, 'w');
if ( false !== $local_file) {
if ( false !== fwrite( $local_file, $body)) {
fclose( $local_file);
$sizeinfo = getimagesize( $filepath);
return array('filepath' => realpath( $filepath), 'width' => $sizeinfo[0], 'height' => $sizeinfo[1], 'orginalfilename' => $originalfilename, 'filename' => pathinfo( $filepath, PATHINFO_BASENAME));
}
}
}
}
return false;
}
经测试,此函数能应付绝大多数类型远程图片下载的处理。