first commit
This commit is contained in:
commit
309b0eddf1
286
ImageProxy.php
Normal file
286
ImageProxy.php
Normal 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
77
README.md
Normal 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
32
index.php
Normal 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');
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user