You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测file_get_contents超时引发的图片未完整下载问题?

如何检测PHP下载的图片是否完整?

我写了一段从国内服务器下载图片的PHP脚本,由于CDN因素,经常出现图片仅部分下载(显示不全)的情况——推测是超时导致的。但尴尬的是,这种情况下$content并不会等于FALSE,原来的判断逻辑完全失效。请问有什么方法可以检测图片是否未完整下载?

当前使用的代码如下:

function addNewFile($url) { 
    $newFileName = 'newFileName.png'; 
    $content = @file_get_contents($url); 
    if ($content === FALSE) { 
        echo ' - sleep 30 seconds and try again... ' . "\n"; 
        sleep(30); 
        return addNewFile($url); 
    } else { 
        file_put_contents($newFileNameInLocal, $content); 
    } 
}

预期下载的正确图片:
正确图片

实际下载到的不完整图片:
不完整图片


这个场景我太熟悉了!用file_get_contents下载资源时,遇到网络不稳定或者CDN的限流策略,很容易出现“下载了一部分但没报错”的情况。给你几个靠谱的检测思路,按优先级排序:

1. 对比远程文件与本地内容的字节大小

最直接的方式是先获取远程图片的实际大小,再和下载后的内容长度对比。

实现方式:通过get_headers获取Content-Length

function getRemoteFileSize($url) {
    $headers = get_headers($url, true);
    // 检查是否返回了Content-Length字段
    if (isset($headers['Content-Length'])) {
        return (int)$headers['Content-Length'];
    }
    return false;
}

function addNewFile($url) { 
    $newFileName = 'newFileName.png'; 
    $remoteSize = getRemoteFileSize($url);
    
    $content = @file_get_contents($url); 
    if ($content === FALSE) { 
        echo ' - sleep 30 seconds and try again... ' . "\n"; 
        sleep(30); 
        return addNewFile($url); 
    } else {
        // 对比大小(如果能获取到远程大小)
        if ($remoteSize !== false && strlen($content) != $remoteSize) {
            echo ' - Incomplete download, retry...' . "\n";
            sleep(30);
            return addNewFile($url);
        }
        file_put_contents($newFileName, $content);
    } 
}

注意:有些服务器或CDN可能不会返回Content-Length(比如采用分块传输),这时候这个方法就失效了,需要搭配其他方案。

2. 用图片处理库验证完整性

不完整的图片无法被图片解析库正常识别,我们可以利用这一点来检测。比如用PHP内置的GD扩展:

function isImageComplete($imageContent) {
    // 把内容写入临时文件(或者用内存流)
    $tmpFile = tmpfile();
    fwrite($tmpFile, $imageContent);
    $meta = stream_get_meta_data($tmpFile);
    $tmpPath = $meta['uri'];
    
    // 尝试获取图片信息,失败则说明不完整
    $imageInfo = getimagesize($tmpPath);
    fclose($tmpFile);
    
    return $imageInfo !== false;
}

function addNewFile($url) { 
    $newFileName = 'newFileName.png'; 
    $content = @file_get_contents($url); 
    if ($content === FALSE) { 
        echo ' - sleep 30 seconds and try again... ' . "\n"; 
        sleep(30); 
        return addNewFile($url); 
    } else {
        // 验证图片完整性
        if (!isImageComplete($content)) {
            echo ' - Corrupted image, retry...' . "\n";
            sleep(30);
            return addNewFile($url);
        }
        file_put_contents($newFileName, $content);
    } 
}

这个方法兼容性更强,不管服务器是否返回Content-Length都能生效,缺点是需要GD扩展启用(大部分环境都默认开启)。

3. 改用CURL替代file_get_contents,增强超时控制

file_get_contents的超时设置比较简陋,改用CURL可以更精细地控制连接超时、读取超时,同时能直接获取传输的字节数,方便对比:

function curlDownload($url, $timeout = 30) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // 设置连接超时(秒)
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    // 设置读取超时(秒)
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    // 跟随重定向
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    // 获取响应头
    curl_setopt($ch, CURLOPT_HEADER, true);
    
    $response = curl_exec($ch);
    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $headers = substr($response, 0, $headerSize);
    $content = substr($response, $headerSize);
    
    // 获取远程文件大小和实际下载大小
    $remoteSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
    $downloadedSize = strlen($content);
    
    $error = curl_errno($ch) ? curl_error($ch) : null;
    curl_close($ch);
    
    return [
        'content' => $content,
        'is_complete' => $remoteSize == $downloadedSize,
        'error' => $error
    ];
}

function addNewFile($url) { 
    $newFileName = 'newFileName.png'; 
    $result = curlDownload($url);
    
    if ($result['error'] || !$result['is_complete']) { 
        echo ' - Download failed or incomplete, retry after 30s... ' . "\n"; 
        sleep(30); 
        return addNewFile($url); 
    } else {
        file_put_contents($newFileName, $result['content']);
    } 
}

CURL的优势在于更稳定的超时控制,以及更丰富的传输信息,适合在复杂网络环境下使用。


额外建议

  • 给重试逻辑加个次数限制,比如最多重试3次,避免无限递归导致栈溢出;
  • 针对不同图片格式(JPG/PNG/GIF),可以调整验证逻辑(比如GIF的多帧检测);
  • 如果CDN经常出现分段传输的情况,优先用图片解析的方式验证完整性。

内容的提问来源于stack exchange,提问作者Pak Ho Cheung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:09:46