码桶

发现社区成员的开源项目

开心 /

kxpyq

公开
main
kxpyq/img-proxy.php
img-proxy.php5.2 KB
<?php
/**
 * 图片缩放代理
 * 用法: img-proxy.php?url=图片地址&w=800
 * 缩放后缓存到 assets/cache/
 */

// GD 扩展检测
if (!function_exists('gd_info')) {
    // 无 GD 时直接 302 跳转到原图
    $raw_url = @$_GET['url'];
    if ($raw_url && preg_match('#^https?://#i', $raw_url)) {
        header('Location: ' . $raw_url);
    } else {
        http_response_code(500);
        echo 'GD extension not installed';
    }
    exit;
}

header('Cache-Control: public, max-age=2592000'); // 30天缓存

$url = @$_GET['url'];
$max_w = intval(@$_GET['w']) ?: 800;
$max_h = intval(@$_GET['h']) ?: 0;

if (empty($url)) { http_response_code(400); exit('Missing url'); }
if (!preg_match('#^https?://#i', $url)) { http_response_code(400); exit('Invalid url'); }

// 缓存目录
$cache_dir = __DIR__ . '/assets/cache';
if (!is_dir($cache_dir)) { @mkdir($cache_dir, 0755, true); }

// 缓存文件名
$hash = md5($url) . '_' . $max_w . 'x' . $max_h;
$ext = strtolower(pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
if (!in_array($ext, ['jpg','jpeg','png','gif','webp','bmp'])) { $ext = 'jpg'; }
$cache_file = $cache_dir . '/' . $hash . '.' . $ext;

// 命中缓存
if (file_exists($cache_file) && filesize($cache_file) > 0) {
    $mime = ['jpg'=>'image/jpeg','jpeg'=>'image/jpeg','png'=>'image/png','gif'=>'image/gif','webp'=>'image/webp','bmp'=>'image/bmp'];
    header('Content-Type: ' . ($mime[$ext] ?? 'image/jpeg'));
    header('Age: ' . (time() - filemtime($cache_file)));
    readfile($cache_file);
    exit;
}

// 下载原图(禁gzip)
$img_data = @file_get_contents($url, false, stream_context_create([
    'http'=>[
        'timeout'=>10,
        'user-agent'=>'Mozilla/5.0 (compatible; KXpyq/1.0)',
        'follow_location'=>true,
        'max_redirects'=>3,
        'header'=>"Accept-Encoding: identity\r\n"
    ]
]));

// 兜底:如果还是gzip了就解压
if ($img_data && strlen($img_data) > 2 && @substr($img_data, 0, 2) === "\x1f\x8b") {
    $decompressed = @gzdecode($img_data);
    if ($decompressed !== false) $img_data = $decompressed;
}

if ($img_data === false || strlen($img_data) < 100) {
    header('Location: ' . $url);
    exit;
}

// 创建图片对象
$src = @imagecreatefromstring($img_data);
if (!$src) {
    header('Content-Length: ' . strlen($img_data));
    echo $img_data;
    exit;
}

$orig_w = imagesx($src);
$orig_h = imagesy($src);

// ===== 修复 EXIF 方向 =====
if (function_exists('exif_read_data') && in_array($ext, ['jpg','jpeg'])) {
    $tmpfile = tempnam(sys_get_temp_dir(), 'pxy');
    file_put_contents($tmpfile, $img_data);
    $exif = @exif_read_data($tmpfile, 'IFD0');
    @unlink($tmpfile);
    $orientation = $exif['Orientation'] ?? 1;
    switch ($orientation) {
        case 2: // 水平翻转
            imageflip($src, IMG_FLIP_HORIZONTAL);
            break;
        case 3: // 旋转180
            $src = imagerotate($src, 180, 0);
            break;
        case 4: // 垂直翻转
            imageflip($src, IMG_FLIP_VERTICAL);
            break;
        case 5: // 顺时针90 + 水平翻转
            $src = imagerotate($src, 270, 0);
            imageflip($src, IMG_FLIP_HORIZONTAL);
            break;
        case 6: // 顺时针90 (最常见的竖拍)
            $src = imagerotate($src, 270, 0);
            break;
        case 7: // 逆时针90 + 水平翻转
            $src = imagerotate($src, 90, 0);
            imageflip($src, IMG_FLIP_HORIZONTAL);
            break;
        case 8: // 逆时针90
            $src = imagerotate($src, 90, 0);
            break;
    }
    // 旋转后重新获取宽高
    if (in_array($orientation, [5,6,7,8])) {
        $orig_w = imagesy($src);
        $orig_h = imagesx($src);
    }
}
// ===== EXIF 修复结束 =====

// 计算缩放尺寸
$dst_w = $orig_w;
$dst_h = $orig_h;

if ($max_w > 0 && $dst_w > $max_w) {
    $ratio = $max_w / $dst_w;
    $dst_w = $max_w;
    $dst_h = intval($orig_h * $ratio);
}
if ($max_h > 0 && $dst_h > $max_h) {
    $ratio = $max_h / $dst_h;
    $dst_h = $max_h;
    $dst_w = intval($dst_w * $ratio);
}

// 尺寸没变,直接缓存原图
if ($dst_w == $orig_w && $dst_h == $orig_h) {
    file_put_contents($cache_file, $img_data);
    $mime = ['jpg'=>'image/jpeg','jpeg'=>'image/jpeg','png'=>'image/png','gif'=>'image/gif','webp'=>'image/webp','bmp'=>'image/bmp'];
    header('Content-Type: ' . ($mime[$ext] ?? 'image/jpeg'));
    header('Content-Length: ' . strlen($img_data));
    echo $img_data;
    imagedestroy($src);
    exit;
}

// 缩放
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagealphablending($dst, false);
imagesavealpha($dst, true);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $dst_w, $dst_h, $orig_w, $orig_h);

// 输出并缓存
ob_start();
switch ($ext) {
    case 'png':  header('Content-Type: image/png');  imagepng($dst, null, 6); break;
    case 'gif':  header('Content-Type: image/gif');   imagegif($dst); break;
    case 'webp': header('Content-Type: image/webp');  imagewebp($dst, null, 80); break;
    default:     header('Content-Type: image/jpeg');   imagejpeg($dst, null, 85); break;
}
$img_out = ob_get_clean();

@file_put_contents($cache_file, $img_out);
header('Content-Length: ' . strlen($img_out));
echo $img_out;

imagedestroy($src);
imagedestroy($dst);