家明故事

HTTPS网站显示HTTP链接图片解决方案(PHP代理图片)

家明 0 0

对于HTTPS网站显示HTTP链接图片,解决方案使用Apache和PHP实现HTTP图片代理的方案如下:

基础代理 proxy_simple.php

php
<?php
// proxy_simple.php - 修复版图片代理
header('Content-Type: image/png');
header('Cache-Control: public, max-age=86400');
header('Access-Control-Allow-Origin: *');

// 获取URL参数
$imageUrl = isset($_GET['url']) ? urldecode($_GET['url']) : '';

if (empty($imageUrl)) {
    showError('缺少URL参数');
    exit;
}

// 验证域名
$allowedDomains = ['image-site.com'];
$host = parse_url($imageUrl, PHP_URL_HOST);

if (!in_array($host, $allowedDomains)) {
    showError('不允许的域名: ' . $host);
    exit;
}

// 确保使用HTTP协议
if (strpos($imageUrl, 'http') === false) {
    $imageUrl = 'http://' . $imageUrl;
}

// 使用file_get_contents或cURL获取图片
$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n" .
                   "Accept: image/*\r\n" .
                   "Referer: " . ($_SERVER['HTTP_REFERER'] ?? '') . "\r\n",
        'timeout' => 10
    ]
]);

try {
    // 方法1: 使用file_get_contents
    $imageData = @file_get_contents($imageUrl, false, $context);
    
    if ($imageData === false) {
        // 方法2: 如果失败,尝试使用cURL
        $imageData = fetchWithCurl($imageUrl);
    }
    
    if ($imageData) {
        // 检测MIME类型
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_buffer($finfo, $imageData);
        finfo_close($finfo);
        
        header("Content-Type: $mimeType");
        echo $imageData;
    } else {
        showError('无法获取图片数据');
    }
} catch (Exception $e) {
    showError('代理错误: ' . $e->getMessage());
}

function fetchWithCurl($url) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 3,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        CURLOPT_REFERER => $_SERVER['HTTP_REFERER'] ?? '',
    ]);
    
    $result = curl_exec($ch);
    curl_close($ch);
    
    return $result;
}

function showError($message) {
    // 确保没有其他输出
    if (ob_get_length()) ob_clean();
    
    // 设置正确的Content-Type
    header('Content-Type: image/png');
    
    $width = 400;
    $height = 100;
    
    // 创建图片资源
    $image = imagecreatetruecolor($width, $height);
    
    if (!$image) {
        // 如果创建图片失败,输出纯文本错误
        header('Content-Type: text/plain; charset=utf-8');
        echo "错误: $message\n无法生成错误图片";
        exit;
    }
    
    // 设置颜色
    $bgColor = imagecolorallocate($image, 245, 245, 245);
    $borderColor = imagecolorallocate($image, 200, 200, 200);
    $textColor = imagecolorallocate($image, 180, 0, 0);
    
    // 填充背景
    imagefilledrectangle($image, 0, 0, $width, $height, $bgColor);
    
    // 添加边框
    imagerectangle($image, 0, 0, $width-1, $height-1, $borderColor);
    
    // 方法1: 使用imagestring显示英文错误信息
    $errorText = 'Image Proxy Error';
    $textX = ($width - strlen($errorText) * imagefontwidth(5)) / 2;
    imagestring($image, 5, $textX, 20, $errorText, $textColor);
    
    // 处理错误消息,如果是中文,显示简化信息
    $errorMsg = $message;
    
    // 如果包含中文字符,使用简化的错误提示
    if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $message)) {
        // 截取前部分显示
        $errorMsg = 'Error loading image';
    } else {
        // 如果是英文,截断以避免过长
        $errorMsg = substr($message, 0, 40);
    }
    
    // 显示错误详情
    $detailX = ($width - strlen($errorMsg) * imagefontwidth(4)) / 2;
    imagestring($image, 4, $detailX, 50, $errorMsg, $textColor);
    
    // 输出图片
    imagepng($image);
    imagedestroy($image);
    
    exit;
}

使用GD库显示中文字符的版本(需要中文字体文件)

php
<?php
// proxy_with_chinese.php - 支持中文显示的代理

// 错误报告
error_reporting(0); // 关闭错误显示,避免污染图片输出

// 设置默认时区
date_default_timezone_set('Asia/Shanghai');

function showError($message) {
    // 清除之前的输出
    if (ob_get_length()) ob_clean();
    
    // 设置响应头
    header('Content-Type: image/png');
    header('Cache-Control: no-cache, no-store, must-revalidate');
    
    $width = 400;
    $height = 120;
    
    // 创建图片
    $image = imagecreatetruecolor($width, $height);
    
    if (!$image) {
        // 如果创建图片失败,输出纯文本错误
        header('Content-Type: text/plain; charset=utf-8');
        echo "错误: $message\n无法生成错误图片";
        exit;
    }
    
    // 设置颜色
    $bgColor = imagecolorallocate($image, 250, 250, 250); // 浅灰色背景
    $borderColor = imagecolorallocate($image, 220, 220, 220); // 边框颜色
    $titleColor = imagecolorallocate($image, 220, 0, 0); // 标题颜色
    $textColor = imagecolorallocate($image, 100, 100, 100); // 正文颜色
    $lineColor = imagecolorallocate($image, 200, 200, 200); // 分割线颜色
    
    // 填充背景
    imagefilledrectangle($image, 0, 0, $width, $height, $bgColor);
    
    // 添加边框
    imagerectangle($image, 0, 0, $width-1, $height-1, $borderColor);
    
    // 添加标题栏
    imagefilledrectangle($image, 0, 0, $width, 30, imagecolorallocate($image, 230, 230, 230));
    imagestring($image, 5, 10, 8, '图片代理错误', $titleColor);
    
    // 添加分割线
    imageline($image, 0, 30, $width, 30, $lineColor);
    
    // 显示错误信息(英文)
    $errorTitle = 'Error Details:';
    imagestring($image, 4, 20, 45, $errorTitle, $textColor);
    
    // 处理错误消息
    $errorMsg = substr($message, 0, 50);
    $lines = str_split($errorMsg, 30); // 每行30个字符
    
    $y = 70;
    foreach ($lines as $line) {
        imagestring($image, 4, 20, $y, $line, $textColor);
        $y += 15;
    }
    
    // 添加时间戳
    $timestamp = date('Y-m-d H:i:s');
    imagestring($image, 3, 20, $height - 25, "Time: $timestamp", imagecolorallocate($image, 150, 150, 150));
    
    // 输出图片
    imagepng($image);
    imagedestroy($image);
    
    exit;
}

// 主程序开始
header('Content-Type: image/png');
header('Cache-Control: public, max-age=86400');
header('Access-Control-Allow-Origin: *');

// 获取URL参数
$imageUrl = isset($_GET['url']) ? urldecode($_GET['url']) : '';

if (empty($imageUrl)) {
    showError('缺少URL参数');
    exit;
}

// 验证域名
$allowedDomains = ['image-site.com'];
$host = parse_url($imageUrl, PHP_URL_HOST);

if (!in_array($host, $allowedDomains)) {
    showError('不允许的域名: ' . $host);
    exit;
}

// 确保使用HTTP协议
if (strpos($imageUrl, 'http') === false) {
    $imageUrl = 'http://' . $imageUrl;
}

// 使用cURL获取图片
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $imageUrl,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_MAXREDIRS => 3,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 15,
    CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    CURLOPT_REFERER => $_SERVER['HTTP_REFERER'] ?? '',
    CURLOPT_SSL_VERIFYPEER => false, // 如果目标站点有SSL问题,可以禁用验证
    CURLOPT_SSL_VERIFYHOST => false,
]);

$imageData = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$error = curl_error($ch);
curl_close($ch);

if ($imageData && $httpCode == 200) {
    // 检测MIME类型
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_buffer($finfo, $imageData);
    finfo_close($finfo);
    
    if (strpos($mimeType, 'image/') === 0) {
        header("Content-Type: $mimeType");
        echo $imageData;
    } else {
        showError('获取的不是有效的图片文件');
    }
} else {
    showError('无法获取图片数据: ' . ($error ?: "HTTP $httpCode"));
}

最简单的错误图片生成方案

php
<?php
// proxy_clean.php - 简洁版本

function showCleanError($message) {
    // 清除之前的输出
    while (ob_get_level()) ob_end_clean();
    
    header('Content-Type: image/png');
    
    $width = 400;
    $height = 100;
    
    // 创建图片
    $img = imagecreate($width, $height);
    
    // 颜色
    $white = imagecolorallocate($img, 255, 255, 255);
    $black = imagecolorallocate($img, 0, 0, 0);
    $red = imagecolorallocate($img, 255, 0, 0);
    $gray = imagecolorallocate($img, 200, 200, 200);
    
    // 填充背景
    imagefilledrectangle($img, 0, 0, $width, $height, $white);
    
    // 边框
    imagerectangle($img, 0, 0, $width-1, $height-1, $gray);
    
    // 简单错误提示(只显示英文,避免中文乱码)
    $error_lines = [
        'Image Load Error',
        'URL: ' . ($_GET['url'] ?? ''),
        'Time: ' . date('H:i:s')
    ];
    
    $y = 15;
    foreach ($error_lines as $line) {
        // 居中显示
        $len = strlen($line);
        $x = ($width - $len * imagefontwidth(4)) / 2;
        imagestring($img, 4, $x, $y, $line, $red);
        $y += 20;
    }
    
    // 显示通用错误信息
    imagestring($img, 3, 50, $y+10, 'Please check the image URL and try again', $black);
    
    imagepng($img);
    imagedestroy($img);
    exit;
}

// 主程序
header('Content-Type: image/png');
header('Cache-Control: public, max-age=3600');

$url = $_GET['url'] ?? '';

if (empty($url)) {
    showCleanError('No URL provided');
}

// 验证URL
$host = parse_url($url, PHP_URL_HOST);
if ($host !== 'image-site.com') {
    showCleanError('Invalid domain');
}

// 获取图片
$data = @file_get_contents($url);

if ($data === false) {
    // 尝试cURL
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');
    $data = curl_exec($ch);
    curl_close($ch);
}

if ($data) {
    // 输出图片
    echo $data;
} else {
    showCleanError('Failed to load image');
}

使用SVG作为错误图片(推荐,支持中文)

php
<?php
// proxy_svg_error.php - 使用SVG显示错误

function showSvgError($message) {
    // 清除之前的输出
    while (ob_get_level()) ob_end_clean();
    
    header('Content-Type: image/svg+xml');
    header('Cache-Control: no-cache');
    
    // 对消息进行HTML转义
    $escapedMsg = htmlspecialchars(substr($message, 0, 100), ENT_QUOTES, 'UTF-8');
    
    // 生成SVG
    echo '<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg width="400" height="120" xmlns="http://www.w3.org/2000/svg">
        <rect x="0" y="0" width="400" height="120" fill="#f8f8f8" stroke="#ccc" stroke-width="1"/>
        
        <!-- 标题栏 -->
        <rect x="0" y="0" width="400" height="30" fill="#e8e8e8"/>
        <text x="10" y="20" font-family="Arial, sans-serif" font-size="14" fill="#c00">图片代理错误</text>
        
        <!-- 错误信息 -->
        <text x="10" y="55" font-family="Arial, sans-serif" font-size="12" fill="#333">错误信息:</text>
        
        <!-- 多行文本显示 -->
        <g transform="translate(10, 70)">
            <text font-family="Arial, sans-serif" font-size="11" fill="#666">' . $escapedMsg . '</text>
        </g>
        
        <!-- 底部信息 -->
        <text x="10" y="110" font-family="Arial, sans-serif" font-size="10" fill="#999">时间: ' . date('Y-m-d H:i:s') . '</text>
    </svg>';
    
    exit;
}

// 主程序
header('Content-Type: image/png');
header('Cache-Control: public, max-age=86400');

$url = $_GET['url'] ?? '';

if (empty($url)) {
    showSvgError('缺少URL参数');
}

// 验证域名
$host = parse_url($url, PHP_URL_HOST);
if ($host !== 'image-site.com') {
    showSvgError('不允许的域名: ' . $host);
}

// 获取图片
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_USERAGENT => 'Mozilla/5.0',
]);

$imageData = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($imageData && $httpCode == 200) {
    // 检测MIME类型
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_buffer($finfo, $imageData);
    finfo_close($finfo);
    
    if (strpos($mimeType, 'image/') === 0) {
        header("Content-Type: $mimeType");
        echo $imageData;
    } else {
        showSvgError('获取的不是有效的图片文件');
    }
} else {
    showSvgError('无法获取图片数据 (HTTP ' . $httpCode . ')');
}

推荐的解决方案

  1. 如果不需要显示中文错误信息:使用第一个修复版 proxy_simple.php

  2. 如果需要显示中文错误信息:使用SVG版本 proxy_svg_error.php

  3. 最简单的方案:使用 proxy_clean.php

测试方法

bash
# 测试正常图片
curl "https://your-site.com/proxy_simple.php?url=http://image-site.com/e368d0d1098ad0e641230184a3c9.png"

# 测试错误情况(缺少URL)
curl "https://your-site.com/proxy_simple.php"

# 测试错误情况(错误域名)
curl "https://your-site.com/proxy_simple.php?url=http://wrong-domain.com/image.jpg"

选择最适合你需求的版本即可。SVG版本支持中文且不会乱码,是最佳选择。

标签:PHP  HTTPS  代理图片  

打赏

发表评论