first commit

This commit is contained in:
Snowz 2025-04-14 17:13:00 +08:00
commit 309b0eddf1
3 changed files with 395 additions and 0 deletions

286
ImageProxy.php Normal file
View File

@ -0,0 +1,286 @@
<?php
/**
* 图片代理与缓存系统
*
* 提供安全的图片代理和缓存服务,支持多种图片格式,具有完善的错误处理和日志记录功能
*/
class ImageProxy {
// 支持的图片类型
private const SUPPORTED_TYPES = [
'gif' => 'image/gif',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',
'webp' => 'image/webp',
'bmp' => 'image/bmp',
'svg' => 'image/svg+xml'
];
// 默认配置
private const DEFAULT_CONFIG = [
'cache_dir' => 'cache',
'timeout' => 30,
'connect_timeout' => 15,
'max_redirects' => 5,
'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
];
private $config;
private $logger;
/**
* 构造函数
*
* @param array $config 配置参数
*/
public function __construct(array $config = []) {
$this->config = array_merge(self::DEFAULT_CONFIG, $config);
$this->initCacheDir();
$this->initLogger();
}
/**
* 初始化缓存目录
*/
private function initCacheDir() {
if (!file_exists($this->config['cache_dir'])) {
mkdir($this->config['cache_dir'], 0755, true);
}
}
/**
* 初始化日志记录器
*/
private function initLogger() {
$logDir = $this->config['cache_dir'] . '/logs';
if (!file_exists($logDir)) {
mkdir($logDir, 0755, true);
}
$this->logger = new Logger($logDir);
}
/**
* 处理图片请求
*
* @param string $url 图片URL
* @return void
*/
public function processRequest(string $url) {
try {
if (!$this->validateUrl($url)) {
throw new Exception('Invalid URL format');
}
$cachePath = $this->getCachePath($url);
// 检查缓存
if ($this->isCached($cachePath)) {
$this->serveFromCache($cachePath);
return;
}
// 获取远程图片
$imageData = $this->fetchRemoteImage($url);
// 验证图片数据
if (!$this->validateImageData($imageData)) {
throw new Exception('Invalid image data');
}
// 保存到缓存
$this->saveToCache($cachePath, $imageData);
// 输出图片
$this->outputImage($imageData, $this->getImageType($url));
} catch (Exception $e) {
$this->logger->error('Image processing error: ' . $e->getMessage());
$this->outputError();
}
}
/**
* 验证URL格式
*
* @param string $url URL地址
* @return bool
*/
private function validateUrl(string $url): bool {
return filter_var($url, FILTER_VALIDATE_URL) !== false &&
preg_match('/^https?:\/\//i', $url);
}
/**
* 获取缓存路径
*
* @param string $url 图片URL
* @return string
*/
private function getCachePath(string $url): string {
$parsedUrl = parse_url($url);
$domain = $parsedUrl['host'];
$path = $parsedUrl['path'];
$cacheSubDir = $this->config['cache_dir'] . '/' . $domain;
if (!file_exists($cacheSubDir)) {
mkdir($cacheSubDir, 0755, true);
}
return $cacheSubDir . '/' . md5($url) . '.' . pathinfo($path, PATHINFO_EXTENSION);
}
/**
* 检查图片是否已缓存
*
* @param string $cachePath 缓存路径
* @return bool
*/
private function isCached(string $cachePath): bool {
return file_exists($cachePath) &&
(time() - filemtime($cachePath)) < 86400; // 缓存24小时
}
/**
* 从缓存输出图片
*
* @param string $cachePath 缓存路径
*/
private function serveFromCache(string $cachePath) {
$type = $this->getImageType($cachePath);
header("Content-type: " . $type);
readfile($cachePath);
}
/**
* 获取远程图片
*
* @param string $url 图片URL
* @return string
*/
private function fetchRemoteImage(string $url): string {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => $this->config['max_redirects'],
CURLOPT_CONNECTTIMEOUT => $this->config['connect_timeout'],
CURLOPT_TIMEOUT => $this->config['timeout'],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => $this->config['user_agent'],
CURLOPT_REFERER => parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . '/',
CURLOPT_HTTPHEADER => ['Accept: image/*']
]);
$data = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || empty($data)) {
throw new Exception('Failed to fetch image: HTTP ' . $httpCode);
}
return $data;
}
/**
* 验证图片数据
*
* @param string $data 图片数据
* @return bool
*/
private function validateImageData(string $data): bool {
if (empty($data)) {
return false;
}
// 检查文件头
$headers = [
'image/jpeg' => "\xFF\xD8\xFF",
'image/png' => "\x89PNG\r\n\x1a\n",
'image/gif' => "GIF",
'image/webp' => "RIFF....WEBP",
'image/bmp' => "BM",
'image/svg+xml' => '<?xml'
];
foreach ($headers as $type => $header) {
if (strpos($data, $header) === 0) {
return true;
}
}
return false;
}
/**
* 保存到缓存
*
* @param string $cachePath 缓存路径
* @param string $data 图片数据
*/
private function saveToCache(string $cachePath, string $data) {
file_put_contents($cachePath, $data);
}
/**
* 输出图片
*
* @param string $data 图片数据
* @param string $type 图片类型
*/
private function outputImage(string $data, string $type) {
header("Content-type: " . $type);
header("Cache-Control: public, max-age=86400");
echo $data;
}
/**
* 获取图片类型
*
* @param string $url 图片URL
* @return string
*/
private function getImageType(string $url): string {
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
return self::SUPPORTED_TYPES[$ext] ?? 'image/jpeg';
}
/**
* 输出错误信息
*/
private function outputError() {
header("HTTP/1.1 500 Internal Server Error");
header("Content-type: image/jpeg");
// 输出一个1x1的透明图片
echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
}
}
/**
* 简单的日志记录类
*/
class Logger {
private $logDir;
public function __construct(string $logDir) {
$this->logDir = $logDir;
}
public function error(string $message) {
$this->log('ERROR', $message);
}
public function info(string $message) {
$this->log('INFO', $message);
}
private function log(string $level, string $message) {
$date = date('Y-m-d');
$time = date('Y-m-d H:i:s');
$logFile = $this->logDir . "/{$date}.log";
$logMessage = "[{$time}] [{$level}] {$message}\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
}
}

77
README.md Normal file
View File

@ -0,0 +1,77 @@
# 图片代理与缓存系统
这是一个用于代理和缓存远程图片的系统,主要功能包括:
1. 图片代理通过URL参数获取远程图片并返回
2. 图片缓存:自动缓存已访问的图片,提高访问速度
3. 防盗链支持支持设置Referer解决部分网站的防盗链问题
## 使用方法
在浏览器中访问:
```
/index.php?url=https://example.com/image.jpg
```
## 参数说明
- url: 必填参数需要代理的远程图片URL
## 技术特点
- 支持多种图片格式GIF、JPEG、PNG、WebP、BMP、SVG
- 自动创建缓存目录结构
- 支持HTTPS图片获取
- 自动处理重定向
- 合理的超时设置
- 完善的错误处理和日志记录
- 图片数据验证
- 缓存过期控制
## 安全特性
- 输入验证确保URL以http或https开头
- 文件类型验证:只允许特定图片格式
- 目录权限控制:缓存目录权限设置
- SSL验证支持HTTPS图片获取
- 图片数据验证:验证下载的图片数据是否有效
- 错误处理:完善的错误处理和日志记录
## 性能优化
- 图片缓存机制24小时缓存
- 合理的超时设置
- 内存使用优化
- 并发处理能力
- 缓存控制头:支持浏览器缓存
## 文件说明
- `index.php`: 入口文件,处理请求
- `ImageProxy.php`: 核心类文件,实现图片代理和缓存功能
- `cache/`: 缓存目录
- `cache/logs/`: 日志目录
## 配置说明
可以通过修改`index.php`中的配置参数来调整系统行为:
```php
$proxy = new ImageProxy([
'cache_dir' => 'cache', // 缓存目录
'timeout' => 30, // 请求超时时间(秒)
'connect_timeout' => 15, // 连接超时时间(秒)
'max_redirects' => 5 // 最大重定向次数
]);
```
## 错误处理
系统会自动记录所有错误到日志文件中,日志文件位于`cache/logs/`目录下,按日期命名。
## 注意事项
1. 确保服务器有足够的磁盘空间用于缓存
2. 确保缓存目录有写入权限
3. 建议定期清理缓存文件
4. 建议监控日志文件大小

32
index.php Normal file
View File

@ -0,0 +1,32 @@
<?php
require_once 'ImageProxy.php';
// 设置错误报告
error_reporting(E_ERROR | E_PARSE);
// 设置内存限制
ini_set('memory_limit', '256M');
// 设置执行时间限制
set_time_limit(30);
// 获取URL参数
$url = $_GET['url'] ?? '';
// 创建ImageProxy实例
$proxy = new ImageProxy([
'cache_dir' => 'cache',
'timeout' => 30,
'connect_timeout' => 15,
'max_redirects' => 5
]);
// 处理请求
if (!empty($url)) {
$proxy->processRequest($url);
} else {
// 输出错误信息
header("HTTP/1.1 400 Bad Request");
header("Content-type: image/jpeg");
echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
}