我们在制作网站时很难为页面上的每个栏目、板块都制作相应大小的图片,因为这样非常费时费力,自动生成缩略图也就显得尤为重要,熟悉帝国cms的朋友都知道,帝国cms带有缩略图函数,使用效果尚可,那有没有其他的办法呢,其实有很多。
很多技术人员已经编写了开源的图片缓存生成类用于网站各处图片的缓存,今年我们用到的是timthumb.php,此类可以用来生成图片的缩略图并加以处理,如果在linux环境下安装了optipng或pngcrush工具,也可以进行网站的截图操作,例如网址导航站,这个功能非常实用。下面我们来介绍一下这个缓存类的用法。
访问地址为:http://yuming.com/timthumb.php?src=http://yuming.com/200.jpg&w=200&h=300&q=100&f=3,9|4,2&s=1&ct=1
参数均通过get请求提交,可选参数和说明如下:
src : 需要进行图片缩放的源图片地址,或者是需要进行截图操作的网页地址
webshot : 如果此值为真则进行截图操作
w : 生成图片的宽度,如果宽度或高度只设置了一个值,则根据其中一个值进行等比缩放
h : 生成图片的高度,如果高度和宽度都没有指定,则默认为100*100
zc : 生成图片的缩放模式,可选值0, 1, 2, 3, 默认为1,每个值的不同之处可看下面文件的第100行注释
q : 生成图片的质量,默认90
a : 超出部分的裁剪位置,和缩放模式有关,可选值t, b, l, r, 默认为从顶部裁剪
f : 需要对生成后的图片使用一些过滤器的话,则在这里传不同过滤器的代码和值,具体操作方法可见下面文件的第821行注解
s : 是否对生产的图片进行锐化处理
cc : 生成图片的背景画布颜色
ct : 生成png图片时背景是否透明
下面是这个图片缓存类的完整代码,带有完整的中文注释,非常棒。但是作者我们已经找不到了,感谢他的付出。
该缓存类的官方地址:https://www.binarymoon.co.uk/projects/timthumb/
折叠展开PHP 代码
- <?php
-
- define ('VERSION', '2.8.10');
-
- if( file_exists(dirname(__FILE__) . '/timthumb-config.php')){
- require_once('timthumb-config.php');
- }
-
- if(! defined('DEBUG_ON') ){
- define ('DEBUG_ON', false);
- }
-
- if(! defined('DEBUG_LEVEL') ){
- define ('DEBUG_LEVEL', 1);
- }
-
- if(! defined('MEMORY_LIMIT') ){
- define ('MEMORY_LIMIT', '30M');
- }
-
- if(! defined('BLOCK_EXTERNAL_LEECHERS') ){
- define ('BLOCK_EXTERNAL_LEECHERS', false);
- }
-
- if(! defined('ALLOW_EXTERNAL') ){
- define ('ALLOW_EXTERNAL', TRUE);
- }
-
- if(! defined('ALLOW_ALL_EXTERNAL_SITES') ){
- define ('ALLOW_ALL_EXTERNAL_SITES', false);
- }
-
- if(! defined('FILE_CACHE_ENABLED') ){
- define ('FILE_CACHE_ENABLED', TRUE);
- }
-
- if(! defined('FILE_CACHE_TIME_BETWEEN_CLEANS')){
- define ('FILE_CACHE_TIME_BETWEEN_CLEANS', 86400);
- }
-
- if(! defined('FILE_CACHE_MAX_FILE_AGE') ){
- define ('FILE_CACHE_MAX_FILE_AGE', 86400);
- }
-
- if(! defined('FILE_CACHE_SUFFIX') ){
- define ('FILE_CACHE_SUFFIX', '.timthumb.txt');
- }
-
- if(! defined('FILE_CACHE_PREFIX') ){
- define ('FILE_CACHE_PREFIX', 'timthumb');
- }
-
- if(! defined('FILE_CACHE_DIRECTORY') ){
- define ('FILE_CACHE_DIRECTORY', './cache');
- }
-
- if(! defined('MAX_FILE_SIZE') ){
- define ('MAX_FILE_SIZE', 10485760);
- }
-
- if(! defined('CURL_TIMEOUT') ){
- define ('CURL_TIMEOUT', 20);
- }
-
- if(! defined('WAIT_BETWEEN_FETCH_ERRORS') ){
- define ('WAIT_BETWEEN_FETCH_ERRORS', 3600);
- }
-
- if(! defined('BROWSER_CACHE_MAX_AGE') ){
- define ('BROWSER_CACHE_MAX_AGE', 864000);
- }
-
- if(! defined('BROWSER_CACHE_DISABLE') ){
- define ('BROWSER_CACHE_DISABLE', false);
- }
-
- if(! defined('MAX_WIDTH') ){
- define ('MAX_WIDTH', 1500);
- }
-
- if(! defined('MAX_HEIGHT') ){
- define ('MAX_HEIGHT', 1500);
- }
-
- if(! defined('NOT_FOUND_IMAGE') ){
- define ('NOT_FOUND_IMAGE', '');
- }
-
- if(! defined('ERROR_IMAGE') ){
- define ('ERROR_IMAGE', '');
- }
-
- if(! defined('PNG_IS_TRANSPARENT') ){
- define ('PNG_IS_TRANSPARENT', FALSE);
- }
-
- if(! defined('DEFAULT_Q') ){
- define ('DEFAULT_Q', 90);
- }
-
- if(! defined('DEFAULT_ZC') ){
- define ('DEFAULT_ZC', 1);
- }
-
- if(! defined('DEFAULT_F') ){
- define ('DEFAULT_F', '');
- }
-
- if(! defined('DEFAULT_S') ){
- define ('DEFAULT_S', 0);
- }
-
- if(! defined('DEFAULT_CC') ){
- define ('DEFAULT_CC', 'ffffff');
- }
-
-
- if(! defined('OPTIPNG_ENABLED') ){
- define ('OPTIPNG_ENABLED', false);
- }
- if(! defined('OPTIPNG_PATH') ){
- define ('OPTIPNG_PATH', '/usr/bin/optipng');
- }
- if(! defined('PNGCRUSH_ENABLED') ){
- define ('PNGCRUSH_ENABLED', false);
- }
- if(! defined('PNGCRUSH_PATH') ){
- define ('PNGCRUSH_PATH', '/usr/bin/pngcrush');
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- if(! defined('WEBSHOT_ENABLED') ){
- define ('WEBSHOT_ENABLED', false);
- }
-
- if(! defined('WEBSHOT_CUTYCAPT') ){
- define ('WEBSHOT_CUTYCAPT', '/usr/local/bin/CutyCapt');
- }
-
- if(! defined('WEBSHOT_XVFB') ){
- define ('WEBSHOT_XVFB', '/usr/bin/xvfb-run');
- }
-
- if(! defined('WEBSHOT_SCREEN_X') ){
- define ('WEBSHOT_SCREEN_X', '1024');
- }
-
- if(! defined('WEBSHOT_SCREEN_Y') ){
- define ('WEBSHOT_SCREEN_Y', '768');
- }
-
- if(! defined('WEBSHOT_COLOR_DEPTH') ){
- define ('WEBSHOT_COLOR_DEPTH', '24');
- }
-
- if(! defined('WEBSHOT_IMAGE_FORMAT') ){
- define ('WEBSHOT_IMAGE_FORMAT', 'png');
- }
-
- if(! defined('WEBSHOT_TIMEOUT') ){
- define ('WEBSHOT_TIMEOUT', '20');
- }
-
- if(! defined('WEBSHOT_USER_AGENT') ){
- define ('WEBSHOT_USER_AGENT', "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18");
- }
-
- if(! defined('WEBSHOT_JAVASCRIPT_ON') ){
- define ('WEBSHOT_JAVASCRIPT_ON', true);
- }
-
- if(! defined('WEBSHOT_JAVA_ON') ){
- define ('WEBSHOT_JAVA_ON', false);
- }
-
- if(! defined('WEBSHOT_PLUGINS_ON') ){
- define ('WEBSHOT_PLUGINS_ON', true);
- }
-
- if(! defined('WEBSHOT_PROXY') ){
- define ('WEBSHOT_PROXY', '');
- }
-
- if(! defined('WEBSHOT_XVFB_RUNNING') ){
- define ('WEBSHOT_XVFB_RUNNING', false);
- }
-
- if(! isset($ALLOWED_SITES)){
- $ALLOWED_SITES = array (
- 'flickr.com',
- 'staticflickr.com',
- 'picasa.com',
- 'img.youtube.com',
- 'upload.wikimedia.org',
- 'photobucket.com',
- 'imgur.com',
- 'imageshack.us',
- 'tinypic.com',
- );
- }
-
-
-
-
-
-
- timthumb::start();
-
- class timthumb {
- protected $src = "";
- protected $is404 = false;
- protected $docRoot = "";
- protected $lastURLError = false;
- protected $localImage = "";
- protected $localImageMTime = 0;
- protected $url = false;
- protected $myHost = "";
- protected $isURL = false;
- protected $cachefile = '';
- protected $errors = array();
- protected $toDeletes = array();
- protected $cacheDirectory = '';
- protected $startTime = 0;
- protected $lastBenchTime = 0;
- protected $cropTop = false;
- protected $salt = "";
- protected $fileCacheVersion = 1;
- protected $filePrependSecurityBlock = "<?php die('Execution denied!'); //"; //缓存文件安全头,防止直接访问
- protected static $curlDataWritten = 0;
- protected static $curlFH = false;
-
- public static function start(){
-
- $tim = new timthumb();
-
- $tim->handleErrors();
-
- $tim->securityChecks();
-
- if($tim->tryBrowserCache()){
-
- exit(0);
- }
-
- $tim->handleErrors();
-
- if(FILE_CACHE_ENABLED && $tim->tryServerCache()){
-
- exit(0);
- }
-
- $tim->handleErrors();
-
- $tim->run();
-
- $tim->handleErrors();
-
- exit(0);
- }
-
- public function __construct(){
-
- global $ALLOWED_SITES;
-
- $this->startTime = microtime(true);
-
- date_default_timezone_set('UTC');
-
- $this->debug(1, "Starting new request from " . $this->getIP() . " to " . $_SERVER['REQUEST_URI']);
-
- $this->calcDocRoot();
-
- $this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__);
-
- $this->debug(3, "Salt is: " . $this->salt);
-
- if(FILE_CACHE_DIRECTORY){
-
- if(! is_dir(FILE_CACHE_DIRECTORY)){
-
- @mkdir(FILE_CACHE_DIRECTORY);
-
- if(! is_dir(FILE_CACHE_DIRECTORY)){
-
- $this->error("Could not create the file cache directory.");
- return false;
- }
- }
-
- $this->cacheDirectory = FILE_CACHE_DIRECTORY;
-
- if (!touch($this->cacheDirectory . '/index.html')) {
-
- $this->error("Could not create the index.html file - to fix this create an empty file named index.html file in the cache directory.");
- }
-
- } else {
- $this->cacheDirectory = sys_get_temp_dir();
- }
-
- $this->cleanCache();
-
- $this->myHost = preg_replace('/^www\./i', '', $_SERVER['HTTP_HOST']);
-
- $this->src = $this->param('src');
-
- $this->url = parse_url($this->src);
-
- $this->src = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src);
-
- if(strlen($this->src) <= 3){
-
- $this->error("No image specified");
- return false;
- }
-
- if(BLOCK_EXTERNAL_LEECHERS && array_key_exists('HTTP_REFERER', $_SERVER) && (! preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $_SERVER['HTTP_REFERER']))){
-
- $imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs=");
-
- header('Content-Type: image/gif');
-
- header('Content-Length: ' . sizeof($imgData));
-
- header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
-
- header("Pragma: no-cache");
-
- header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
-
- echo $imgData;
- return false;
-
- exit(0);
- }
-
- if(preg_match('/^https?:\/\/[^\/]+/i', $this->src)){
-
- $this->debug(2, "Is a request for an external URL: " . $this->src);
-
- $this->isURL = true;
-
- } else {
- $this->debug(2, "Is a request for an internal file: " . $this->src);
- }
-
- if($this->isURL && (! ALLOW_EXTERNAL)){
- $this->error("You are not allowed to fetch images from an external website.");
- return false;
- }
-
- if($this->isURL){
-
- if(ALLOW_ALL_EXTERNAL_SITES){
-
- $this->debug(2, "Fetching from all external sites is enabled.");
-
- } else {
-
- $this->debug(2, "Fetching only from selected external sites is enabled.");
-
- $allowed = false;
-
- foreach($ALLOWED_SITES as $site){
-
- if ((strtolower(substr($this->url['host'],-strlen($site)-1)) === strtolower(".$site")) || (strtolower($this->url['host'])===strtolower($site))) {
-
- $this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing.");
-
- $allowed = true;
- }
- }
-
- if(! $allowed){
- return $this->error("You may not fetch images from that site. To enable this site in timthumb, you can either add it to \$ALLOWED_SITES and set ALLOW_EXTERNAL=true. Or you can set ALLOW_ALL_EXTERNAL_SITES=true, depending on your security needs.");
- }
- }
- }
-
- $cachePrefix = ($this->isURL ? '_ext_' : '_int_');
-
- if($this->isURL){
-
- $arr = explode('&', $_SERVER ['QUERY_STRING']);
-
- asort($arr);
-
- $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . implode('', $arr) . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
-
- } else {
-
- $this->localImage = $this->getLocalImagePath($this->src);
-
- if(! $this->localImage){
-
- $this->debug(1, "Could not find the local image: {$this->localImage}");
-
- $this->error("Could not find the internal image you specified.");
-
- $this->set404();
-
- return false;
- }
-
- $this->debug(1, "Local image path is {$this->localImage}");
-
- $this->localImageMTime = @filemtime($this->localImage);
-
- $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER ['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
- }
-
- $this->debug(2, "Cache file is: " . $this->cachefile);
-
- return true;
- }
-
- public function __destruct(){
- foreach($this->toDeletes as $del){
- $this->debug(2, "Deleting temp file $del");
- @unlink($del);
- }
- }
-
- public function run(){
-
- if($this->isURL){
-
- if(! ALLOW_EXTERNAL){
-
- $this->debug(1, "Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg.");
-
- $this->error("You are not allowed to fetch images from an external website.");
-
- return false;
- }
-
- $this->debug(3, "Got request for external image. Starting serveExternalImage.");
-
- if($this->param('webshot')){
-
- if(WEBSHOT_ENABLED){
-
- $this->debug(3, "webshot param is set, so we're going to take a webshot.");
-
- $this->serveWebshot();
-
- } else {
-
- $this->error("You added the webshot parameter but webshots are disabled on this server. You need to set WEBSHOT_ENABLED == true to enable webshots.");
- }
-
- } else {
-
- $this->debug(3, "webshot is NOT set so we're going to try to fetch a regular image.");
-
- $this->serveExternalImage();
-
- }
-
- } else {
-
- $this->debug(3, "Got request for internal image. Starting serveInternalImage()");
-
- $this->serveInternalImage();
- }
-
- return true;
- }
-
- protected function handleErrors(){
-
- if($this->haveErrors()){
-
- if(NOT_FOUND_IMAGE && $this->is404()){
-
- if($this->serveImg(NOT_FOUND_IMAGE)){
- exit(0);
-
- } else {
- $this->error("Additionally, the 404 image that is configured could not be found or there was an error serving it.");
- }
- }
-
- if(ERROR_IMAGE){
-
- if($this->serveImg(ERROR_IMAGE)){
- exit(0);
-
- } else {
- $this->error("Additionally, the error image that is configured could not be found or there was an error serving it.");
- }
- }
-
- $this->serveErrors();
- exit(0);
- }
-
- return false;
- }
-
- protected function tryBrowserCache(){
-
- if(BROWSER_CACHE_DISABLE){
- $this->debug(3, "Browser caching is disabled"); return false;
- }
-
- if(!emptyempty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ){
-
- $this->debug(3, "Got a conditional get");
-
- $mtime = false;
-
- if(! is_file($this->cachefile)){
-
- return false;
- }
-
- if($this->localImageMTime){
-
- $mtime = $this->localImageMTime;
-
- $this->debug(3, "Local real file's modification time is $mtime");
-
- } else if(is_file($this->cachefile)){
-
- $mtime = @filemtime($this->cachefile);
-
- $this->debug(3, "Cached file's modification time is $mtime");
- }
-
- if(! $mtime){ return false; }
-
- $iftime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
-
- $this->debug(3, "The conditional get's if-modified-since unixtime is $iftime");
-
- if($iftime < 1){
-
- $this->debug(3, "Got an invalid conditional get modified since time. Returning false.");
- return false;
- }
-
- if($iftime < $mtime){
-
- $this->debug(3, "File has been modified since last fetch.");
- return false;
-
- } else {
-
- $this->debug(3, "File has not been modified since last get, so serving a 304.");
-
- header ($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
- //记录此次操作,级别1
- $this->debug(1, "Returning 304 not modified");
- //读取成功返回真
- return true;
- }
- }
- //没有读取到缓存,返回假
- return false;
- }
- /*此函数用来运行缓存文件的GC和读取服务器上的缓存文件*/
- protected function tryServerCache(){
- //写日志,记录将读取服务端缓存,级别3
- $this->debug(3, "Trying server cache");
- //如果缓存文件存在
- if(file_exists($this->cachefile)){
- //写日志,记录缓存文件存在
- $this->debug(3, "Cachefile {$this->cachefile} exists");
- //如果请求的是外部图片地址
- if($this->isURL){
- //写日志,记录这是一次外部请求,级别3
- $this->debug(3, "This is an external request, so checking if the cachefile is empty which means the request failed previously.");
- //如果缓存文件的大小小于1,也就是说是一个无效的缓存文件
- if(filesize($this->cachefile) < 1){
- //写日志,记录这是一个空的缓存文件,级别3
- $this->debug(3, "Found an empty cachefile indicating a failed earlier request. Checking how old it is.");
- //如果已到了配置文件中清理无效缓存的时间
- if(time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS){
- //写日志,记录这次删除操作,级别3
- $this->debug(3, "File is older than " . WAIT_BETWEEN_FETCH_ERRORS . " seconds. Deleting and returning false so app can try and load file.");
- //删除此缓存文件
- @unlink($this->cachefile);
- //返回假,说明没有读取到服务端缓存
- return false;
- //否则,空的缓存文件说明上次请求失败,所以要写错误记录
- } else {
- //写日志,记录空的缓存文件依然有效,级别3
- $this->debug(3, "Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host.");
- //设置404错误
- $this->set404();
- //设置错误信息
- $this->error("An error occured fetching image.");
- //返回假代表没有得到缓存
- return false;
- }
- }
- //否则就是正确的缓存文件
- } else {
- //写日志,记录将要直接读取缓存文件,级别3
- $this->debug(3, "Trying to serve cachefile {$this->cachefile}");
- }
- //如果输出图像缓存成功
- if($this->serveCacheFile()){
- //写日志,记录缓存文件信息,级别3
- $this->debug(3, "Succesfully served cachefile {$this->cachefile}");
- return true;
- //如果不成功
- } else {
- //写日志,记录错误信息,级别3
- $this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache.");
- //删除此无效缓存,以便下次请求能重新创建
- @unlink($this->cachefile);
- //同样返回真,因为在serverCacheFile已经记录了错误信息
- return true;
- }
- }
- }
- /*此函数用来记录错误信息*/
- protected function error($err){
- //写记录,记录错误信息,级别3
- $this->debug(3, "Adding error message: $err");
- //记录到错误信息数组
- $this->errors[] = $err;
- return false;
- }
- /*测函数用来检测存储错误信息的数组中是否有内容,也就是说在上一个操作中,是否有错误*/
- protected function haveErrors(){
- if(sizeof($this->errors) > 0){
- return true;
- }
- return false;
- }
- /*此函数输出已存储的错误信息*/
- protected function serveErrors(){
- //设置http头
- header ($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
- //循环输出错误列表信息
- $html = '<ul>';
- foreach($this->errors as $err){
- $html .= '<li>' . htmlentities($err) . '</li>';
- }
- $html .= '</ul>';
- //输出其他错误信息
- echo '<h1>A TimThumb error has occured</h1>The following error(s) occured:<br />' . $html . '<br />';
- echo '<br />Query String : ' . htmlentities ($_SERVER['QUERY_STRING']);
- echo '<br />TimThumb version : ' . VERSION . '</pre>';
- }
- /*此函数用来读取本地图片*/
- protected function serveInternalImage(){
- //写日志,记录本地图片地址
- $this->debug(3, "Local image path is $this->localImage");
- //如果地址无效
- if(! $this->localImage){
- //记录此错误,并退出执行
- $this->sanityFail("localImage not set after verifying it earlier in the code.");
- return false;
- }
- //获取本地图片大小
- $fileSize = filesize($this->localImage);
- //如果本地图片的尺寸超过配置文件的相关设置
- if($fileSize > MAX_FILE_SIZE){
- //记录错误原因,并退出
- $this->error("The file you specified is greater than the maximum allowed file size.");
- return false;
- }
- //如果获取到的图片尺寸无效
- if($fileSize <= 0){
- //记录错误并退出
- $this->error("The file you specified is <= 0 bytes.");
- return false;
- }
- //如果通过了以上验证,则写日志,记录将用processImageAndWriteToCache函数处理本地图片
- $this->debug(3, "Calling processImageAndWriteToCache() for local image.");
- //处理成功则从缓存返回图片
- if($this->processImageAndWriteToCache($this->localImage)){
- $this->serveCacheFile();
- return true;
- //失败则返回假
- } else {
- return false;
- }
- }
- /*此函数用来清理缓存*/
- protected function cleanCache(){
- //如果定义的缓存时间小于0,则退出
- if (FILE_CACHE_TIME_BETWEEN_CLEANS < 0) {
- return;
- }
- //写日志,记录清除缓存操作,级别3
- $this->debug(3, "cleanCache() called");
- //此文件为记录上次进行清除缓存操作的时间戳文件
- $lastCleanFile = $this->cacheDirectory . '/timthumb_cacheLastCleanTime.touch';
-
- //如果上面定义的文件不存在,说明这是第一次进行清除缓存操作,创建此文件并返回空即可
- if(! is_file($lastCleanFile)){
- //写日志,记录创建文件,级别1
- $this->debug(1, "File tracking last clean doesn't exist. Creating $lastCleanFile");
-
- if (!touch($lastCleanFile)) {
-
- $this->error("Could not create cache clean timestamp file.");
- }
- return;
- }
-
- if(@filemtime($lastCleanFile) < (time() - FILE_CACHE_TIME_BETWEEN_CLEANS) ){
-
- $this->debug(1, "Cache was last cleaned more than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago. Cleaning now.");
-
- if (!touch($lastCleanFile)) {
-
- $this->error("Could not create cache clean timestamp file.");
- }
-
- $files = glob($this->cacheDirectory . '/*' . FILE_CACHE_SUFFIX);
-
- if ($files) {
-
- $timeAgo = time() - FILE_CACHE_MAX_FILE_AGE;
-
- foreach($files as $file){
-
- if(@filemtime($file) < $timeAgo){
-
- $this->debug(3, "Deleting cache file $file older than max age: " . FILE_CACHE_MAX_FILE_AGE . " seconds");
- @unlink($file);
- }
- }
- }
- return true;
-
- } else {
-
- $this->debug(3, "Cache was cleaned less than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago so no cleaning needed.");
- }
- return false;
- }
- /*核心函数,处理图片并写入缓存*/
- protected function processImageAndWriteToCache($localImage){
-
- $sData = getimagesize($localImage);
-
- $origType = $sData[2];
-
- $mimeType = $sData['mime'];
-
- $this->debug(3, "Mime type of image is $mimeType");
-
- if(! preg_match('/^image\/(?:gif|jpg|jpeg|png)$/i', $mimeType)){
-
- return $this->error("The image being resized is not a valid gif, jpg or png.");
- }
-
- if (!function_exists ('imagecreatetruecolor')) {
-
- return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library');
- }
-
- if (function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
-
- $imageFilters = array (
-
- 1 => array (IMG_FILTER_NEGATE, 0),
-
- 2 => array (IMG_FILTER_GRAYSCALE, 0),
-
- 3 => array (IMG_FILTER_BRIGHTNESS, 1),
-
- 4 => array (IMG_FILTER_CONTRAST, 1),
-
- 5 => array (IMG_FILTER_COLORIZE, 4),
-
- 6 => array (IMG_FILTER_EDGEDETECT, 0),
-
- 7 => array (IMG_FILTER_EMBOSS, 0),
-
- 8 => array (IMG_FILTER_GAUSSIAN_BLUR, 0),
-
- 9 => array (IMG_FILTER_SELECTIVE_BLUR, 0),
-
- 10 => array (IMG_FILTER_MEAN_REMOVAL, 0),
-
- 11 => array (IMG_FILTER_SMOOTH, 0),
- );
- }
-
-
- $new_width = (int) abs ($this->param('w', 0));
-
- $new_height = (int) abs ($this->param('h', 0));
-
- $zoom_crop = (int) $this->param('zc', DEFAULT_ZC);
-
- $quality = (int) abs ($this->param('q', DEFAULT_Q));
-
- $align = $this->cropTop ? 't' : $this->param('a', 'c');
-
- $filters = $this->param('f', DEFAULT_F);
-
- $sharpen = (bool) $this->param('s', DEFAULT_S);
-
- $canvas_color = $this->param('cc', DEFAULT_CC);
-
- $canvas_trans = (bool) $this->param('ct', '1');
-
-
- if ($new_width == 0 && $new_height == 0) {
- $new_width = 100;
- $new_height = 100;
- }
-
-
- $new_width = min ($new_width, MAX_WIDTH);
- $new_height = min ($new_height, MAX_HEIGHT);
-
-
- $this->setMemoryLimit();
-
-
- $image = $this->openImage ($mimeType, $localImage);
-
- if ($image === false) {
- return $this->error('Unable to open image.');
- }
-
-
- $width = imagesx ($image);
- $height = imagesy ($image);
- $origin_x = 0;
- $origin_y = 0;
-
-
- if ($new_width && !$new_height) {
- $new_height = floor ($height * ($new_width / $width));
- } else if ($new_height && !$new_width) {
- $new_width = floor ($width * ($new_height / $height));
- }
-
-
- if ($zoom_crop == 3) {
-
- $final_height = $height * ($new_width / $width);
-
- if ($final_height > $new_height) {
- $new_width = $width * ($new_height / $height);
- } else {
- $new_height = $final_height;
- }
-
- }
-
-
- $canvas = imagecreatetruecolor ($new_width, $new_height);
-
- imagealphablending ($canvas, false);
-
- if (strlen($canvas_color) == 3) {
-
- $canvas_color = str_repeat(substr($canvas_color, 0, 1), 2) . str_repeat(substr($canvas_color, 1, 1), 2) . str_repeat(substr($canvas_color, 2, 1), 2);
-
- } else if (strlen($canvas_color) != 6) {
- $canvas_color = DEFAULT_CC;
- }
-
- $canvas_color_R = hexdec (substr ($canvas_color, 0, 2));
- $canvas_color_G = hexdec (substr ($canvas_color, 2, 2));
- $canvas_color_B = hexdec (substr ($canvas_color, 4, 2));
-
-
- if(preg_match('/^image\/png$/i', $mimeType) && !PNG_IS_TRANSPARENT && $canvas_trans){
- $color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127);
-
- }else{
- $color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 0);
- }
-
-
- imagefill ($canvas, 0, 0, $color);
-
-
-
- if ($zoom_crop == 2) {
-
- $final_height = $height * ($new_width / $width);
-
- if ($final_height > $new_height) {
-
- $origin_x = $new_width / 2;
-
- $new_width = $width * ($new_height / $height);
-
- $origin_x = round ($origin_x - ($new_width / 2));
-
- } else {
- $origin_y = $new_height / 2;
- $new_height = $final_height;
- $origin_y = round ($origin_y - ($new_height / 2));
-
- }
-
- }
-
-
- imagesavealpha ($canvas, true);
-
-
- if ($zoom_crop > 0) {
-
- $src_x = $src_y = 0;
-
- $src_w = $width;
-
- $src_h = $height;
-
-
- $cmp_x = $width / $new_width;
- $cmp_y = $height / $new_height;
-
-
- if ($cmp_x > $cmp_y) {
- $src_w = round ($width / $cmp_x * $cmp_y);
- $src_x = round (($width - ($width / $cmp_x * $cmp_y)) / 2);
-
- } else if ($cmp_y > $cmp_x) {
-
- $src_h = round ($height / $cmp_y * $cmp_x);
- $src_y = round (($height - ($height / $cmp_y * $cmp_x)) / 2);
-
- }
-
-
- if ($align) {
- if (strpos ($align, 't') !== false) {
- $src_y = 0;
- }
- if (strpos ($align, 'b') !== false) {
- $src_y = $height - $src_h;
- }
- if (strpos ($align, 'l') !== false) {
- $src_x = 0;
- }
- if (strpos ($align, 'r') !== false) {
- $src_x = $width - $src_w;
- }
- }
-
-
- imagecopyresampled ($canvas, $image, $origin_x, $origin_y, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
-
- } else {
-
-
- imagecopyresampled ($canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
-
- }
-
- if ($filters != '' && function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
-
- $filterList = explode ('|', $filters);
- foreach ($filterList as $fl) {
-
- $filterSettings = explode (',', $fl);
-
- if (isset ($imageFilters[$filterSettings[0]])) {
-
- for ($i = 0; $i < 4; $i ++) {
- if (!isset ($filterSettings[$i])) {
- $filterSettings[$i] = null;
- } else {
- $filterSettings[$i] = (int) $filterSettings[$i];
- }
- }
-
- switch ($imageFilters[$filterSettings[0]][1]) {
-
- case 1:
-
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);
- break;
-
- case 2:
-
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);
- break;
-
- case 3:
-
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);
- break;
-
- case 4:
-
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3], $filterSettings[4]);
- break;
-
- default:
-
- imagefilter ($canvas, $imageFilters[$filterSettings[0]][0]);
- break;
-
- }
- }
- }
- }
-
-
- if ($sharpen && function_exists ('imageconvolution')) {
-
- $sharpenMatrix = array (
- array (-1,-1,-1),
- array (-1,16,-1),
- array (-1,-1,-1),
- );
-
- $divisor = 8;
- $offset = 0;
-
- imageconvolution ($canvas, $sharpenMatrix, $divisor, $offset);
-
- }
-
- if ( (IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor( $image ) && imagecolortransparent( $image ) > 0 ){
- imagetruecolortopalette( $canvas, false, imagecolorstotal( $image ) );
- }
-
- $imgType = "";
- $tempfile = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
- if(preg_match('/^image\/(?:jpg|jpeg)$/i', $mimeType)){
- $imgType = 'jpg';
- imagejpeg($canvas, $tempfile, $quality);
- } else if(preg_match('/^image\/png$/i', $mimeType)){
- $imgType = 'png';
- imagepng($canvas, $tempfile, floor($quality * 0.09));
- } else if(preg_match('/^image\/gif$/i', $mimeType)){
- $imgType = 'gif';
- imagegif($canvas, $tempfile);
- } else {
-
- return $this->sanityFail("Could not match mime type after verifying it previously.");
- }
-
- if($imgType == 'png' && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)){
-
- $exec = OPTIPNG_PATH;
-
- $this->debug(3, "optipng'ing $tempfile");
-
- $presize = filesize($tempfile);
-
- $out = `$exec -o1 $tempfile`;
-
- clearstatcache();
-
- $aftersize = filesize($tempfile);
-
- $sizeDrop = $presize - $aftersize;
-
- if($sizeDrop > 0){
- $this->debug(1, "optipng reduced size by $sizeDrop");
- } else if($sizeDrop < 0){
- $this->debug(1, "optipng increased size! Difference was: $sizeDrop");
- } else {
- $this->debug(1, "optipng did not change image size.");
- }
-
- } else if($imgType == 'png' && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)){
- $exec = PNGCRUSH_PATH;
- //和optipng不同的是,pngcrush会将处理完的文件新生成一个文件,所以这里新建个文件
- $tempfile2 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
- //写日志,记录文件名
- $this->debug(3, "pngcrush'ing $tempfile to $tempfile2");
-
- $out = `$exec $tempfile $tempfile2`;
- $todel = "";
-
- if(is_file($tempfile2)){
-
- $sizeDrop = filesize($tempfile) - filesize($tempfile2);
-
- if($sizeDrop > 0){
- $this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction");
- $todel = $tempfile;
- $tempfile = $tempfile2;
-
- } else {
- $this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes.");
- $todel = $tempfile2;
- }
-
- } else {
- $this->debug(3, "pngcrush failed with output: $out");
- $todel = $tempfile2;
- }
-
- @unlink($todel);
- }
-
- $this->debug(3, "Rewriting image with security header.");
-
- $tempfile4 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
-
- $context = stream_context_create ();
-
- $fp = fopen($tempfile,'r',0,$context);
-
- file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>');
-
- file_put_contents($tempfile4, $fp, FILE_APPEND);
-
- fclose($fp);
-
- @unlink($tempfile);
-
- $this->debug(3, "Locking and replacing cache file.");
-
- $lockFile = $this->cachefile . '.lock';
-
- $fh = fopen($lockFile, 'w');
-
- if(! $fh){
- return $this->error("Could not open the lockfile for writing an image.");
- }
-
- if(flock($fh, LOCK_EX)){
-
- @unlink($this->cachefile);
-
- rename($tempfile4, $this->cachefile);
-
- flock($fh, LOCK_UN);
-
- fclose($fh);
-
- @unlink($lockFile);
-
- } else {
-
- fclose($fh);
-
- @unlink($lockFile);
-
- @unlink($tempfile4);
-
- return $this->error("Could not get a lock for writing.");
- }
-
- $this->debug(3, "Done image replace with security header. Cleaning up and running cleanCache()");
-
- imagedestroy($canvas);
- imagedestroy($image);
-
- return true;
- }
-
- protected function calcDocRoot(){
-
- $docRoot = @$_SERVER['DOCUMENT_ROOT'];
-
- if (defined('LOCAL_FILE_BASE_DIRECTORY')) {
- $docRoot = LOCAL_FILE_BASE_DIRECTORY;
- }
-
- if(!isset($docRoot)){
-
- $this->debug(3, "DOCUMENT_ROOT is not set. This is probably windows. Starting search 1.");
-
- if(isset($_SERVER['SCRIPT_FILENAME'])){
- $docRoot = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF'])));
- //写一条记录,说明DOCUMENT_ROOT的值是通过SCRIPT_FILENAME和PHP_SELF来得的,级别3
- $this->debug(3, "Generated docRoot using SCRIPT_FILENAME and PHP_SELF as: $docRoot");
- }
- }
- //如果还是没有获取到文档根目录
- if(!isset($docRoot)){
- //先写一条记录,说明还是没得到DOCUMENT_ROOT,级别3
- $this->debug(3, "DOCUMENT_ROOT still is not set. Starting search 2.");
- //通过PATH_TRANSLATED和PHP_SELF来得到文档根目录,关于PATH_TRANSLATED的说明可以看这里:http://blogs.msdn.com/b/david.wang/archive/2005/08/04/what-is-path-translated.aspx
- if(isset($_SERVER['PATH_TRANSLATED'])){
- $docRoot = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF'])));
- //写记录,说明说明DOCUMENT_ROOT的值是通过PATH_TRANSLATED和PHP_SELF来得的,级别3
- $this->debug(3, "Generated docRoot using PATH_TRANSLATED and PHP_SELF as: $docRoot");
- }
- }
- //如果文档根目录不是服务器根目录,则去掉最后一个 '/'
- if($docRoot && $_SERVER['DOCUMENT_ROOT'] != '/'){ $docRoot = preg_replace('/\/$/', '', $docRoot); }
- //写记录,说明文档根目录的值,级别3
- $this->debug(3, "Doc root is: " . $docRoot);
- //赋值给成员属性
- $this->docRoot = $docRoot;
-
- }
-
- /*此函数用来获取本地图片地址,参数src是相对与文档根目录的地址*/
- protected function getLocalImagePath($src){
- //去掉开头的 /
- $src = ltrim($src, '/');
- //如果前面没有获取到文档根目录,出于安全考虑,那么这里只能对timthumbs.php所在的目录下的图片进行操作
- if(! $this->docRoot){
- //写日志,级别3,说明下面进行图片地址的检查
- $this->debug(3, "We have no document root set, so as a last resort, lets check if the image is in the current dir and serve that.");
- //获取去掉所有路径信息的文件名
- $file = preg_replace('/^.*?([^\/\\\\]+)$/', '$1', $src);
- //如果图片文件和timthumb.php在同一目录下
- if(is_file($file)){
- //返回此图片的路径
- return $this->realpath($file);
- }
- //如果图片文件和timthumb.php不在同一目录下,写错误信息,出于安全考虑,不会允许一个没有文档根目录并且在timthumbs.php所在的目录以外的文件
- return $this->error("Could not find your website document root and the file specified doesn't exist in timthumbs directory. We don't support serving files outside timthumb's directory without a document root for security reasons.");
- }
-
-
- if(file_exists ($this->docRoot . '/' . $src)) {
-
- $this->debug(3, "Found file as " . $this->docRoot . '/' . $src);
-
- $real = $this->realpath($this->docRoot . '/' . $src);
-
- if(stripos($real, $this->docRoot) === 0){
-
- return $real;
- } else {
-
- $this->debug(1, "Security block: The file specified occurs outside the document root.");
- }
- }
-
-
- $absolute = $this->realpath('/' . $src);
-
- if($absolute && file_exists($absolute)){
-
- $this->debug(3, "Found absolute path: $absolute");
-
- if(! $this->docRoot){ $this->sanityFail("docRoot not set when checking absolute path."); }
-
- if(stripos($absolute, $this->docRoot) === 0){
-
- return $absolute;
- } else {
-
- $this->debug(1, "Security block: The file specified occurs outside the document root.");
- }
- }
-
-
- $base = $this->docRoot;
-
-
- if (strstr($_SERVER['SCRIPT_FILENAME'],':')) {
- $sub_directories = explode('\\', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME']));
- } else {
- $sub_directories = explode('/', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME']));
- }
- //遍历子目录数组
- foreach ($sub_directories as $sub){
- //重新组合请求地址
- $base .= $sub . '/';
- //写日志,记录搜索记录,级别3
- $this->debug(3, "Trying file as: " . $base . $src);
- //如果找到了这个文件
- if(file_exists($base . $src)){
- //写日志,记录文件地址,级别3
- $this->debug(3, "Found file as: " . $base . $src);
- //得到实际地址
- $real = $this->realpath($base . $src);
- //如果实际地址的确在本机中,那么返回这个地址
- if(stripos($real, $this->realpath($this->docRoot)) === 0){
- return $real;
- } else {
- //找不到就写日志,没找到,级别1
- $this->debug(1, "Security block: The file specified occurs outside the document root.");
- }
- }
- }
- //还找不到的话,就返回false;
- return false;
- }
- /*此函数用来获得传入文件参数的真实路径*/
- protected function realpath($path){
- //去除路径中带有..的相对路径
- $remove_relatives = '/\w+\/\.\.\
- while(preg_match($remove_relatives,$path)){
- $path = preg_replace($remove_relatives, '', $path);
- }
- //如果去除后路径中仍有..的相对路径,则用realpath函数返回路径,否则直接返回即可
- return preg_match('#^\.\./|/\.\./#', $path) ? realpath($path) : $path;
- }
- /*此函数用来记录在析构函数中需要删除的资源列表*/
- protected function toDelete($name){
- //写日志,记录需要删除的文件信息
- $this->debug(3, "Scheduling file $name to delete on destruct.");
- //添加到待删除数组
- $this->toDeletes[] = $name;
- }
- /*此函数是截图操作的具体实现*/
- protected function serveWebshot(){
- //写日志,记录开始截图操作,级别3
- $this->debug(3, "Starting serveWebshot");
- //一段提示文字,可以到http://code.google.com/p/timthumb/上按照教程进行网站截图设置
- $instr = "Please follow the instructions at http://code.google.com/p/timthumb/ to set your server up for taking website screenshots.";
- //如果CutyCapt不存在
- if(! is_file(WEBSHOT_CUTYCAPT)){
- //退出执行并记录,CutyCapt未被安装
- return $this->error("CutyCapt is not installed. $instr");
- }
- //如果xvfb不存在
- if(! is_file(WEBSHOT_XVFB)){
- //退出执行并记录,xvfb未被安装
- return $this->Error("Xvfb is not installed. $instr");
- }
- //CUTYCAPT地址
- $cuty = WEBSHOT_CUTYCAPT;
- //xvfb地址
- $xv = WEBSHOT_XVFB;
- //截图屏幕宽度
- $screenX = WEBSHOT_SCREEN_X;
- //截图屏幕高度
- $screenY = WEBSHOT_SCREEN_Y;
- //截图色深
- $colDepth = WEBSHOT_COLOR_DEPTH;
- //截图保存格式
- $format = WEBSHOT_IMAGE_FORMAT;
- //截图超时时间,单位ms
- $timeout = WEBSHOT_TIMEOUT * 1000;
- //USER_AGENT头
- $ua = WEBSHOT_USER_AGENT;
- //是否启用js
- $jsOn = WEBSHOT_JAVASCRIPT_ON ? 'on' : 'off';
- //是否启用java
- $javaOn = WEBSHOT_JAVA_ON ? 'on' : 'off';
- //是否启用其他插件
- $pluginsOn = WEBSHOT_PLUGINS_ON ? 'on' : 'off';
- //是否启用代理
- $proxy = WEBSHOT_PROXY ? ' --http-proxy=' . WEBSHOT_PROXY : '';
- //在缓存文件目录,建立一个具有唯一文件名的文件,文件名前缀为timthumb_webshot,用户存储截图后的图片
- $tempfile = tempnam($this->cacheDirectory, 'timthumb_webshot');
- //目标网站地址
- $url = $this->src;
- //验证url合法性
- if(! preg_match('/^https?:\/\/[a-zA-Z0-9\.\-]+/i', $url)){
- //不合法退出执行
- return $this->error("Invalid URL supplied.");
- }
- //过滤掉非法字符
- $url = preg_replace('/[^A-Za-z0-9\-\.\_\~:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=]+/', '', $url);
-
- if(WEBSHOT_XVFB_RUNNING){
-
- putenv('DISPLAY=:100.0');
-
- $command = "$cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile";
-
- } else {
- $command = "$xv --server-args=\"-screen 0, {$screenX}x{$screenY}x{$colDepth}\" $cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile";
- }
-
- $this->debug(3, "Executing command: $command");
-
- $out = `$command`;
-
- $this->debug(3, "Received output: $out");
-
- if(! is_file($tempfile)){
-
- $this->set404();
-
- return $this->error("The command to create a thumbnail failed.");
- }
-
- $this->cropTop = true;
-
- if($this->processImageAndWriteToCache($tempfile)){
-
- $this->debug(3, "Image processed succesfully. Serving from cache");
-
- return $this->serveCacheFile();
-
- } else {
- return false;
- }
- }
-
- protected function serveExternalImage(){
-
- if(! preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+/i', $this->src)){
- $this->error("Invalid URL supplied.");
- return false;
- }
-
- $tempfile = tempnam($this->cacheDirectory, 'timthumb');
-
- $this->debug(3, "Fetching external image into temporary file $tempfile");
-
- $this->toDelete($tempfile);
-
- if(! $this->getURL($this->src, $tempfile)){
-
- @unlink($this->cachefile);
-
- touch($this->cachefile);
-
- $this->debug(3, "Error fetching URL: " . $this->lastURLError);
-
- $this->error("Error reading the URL you specified from remote host." . $this->lastURLError);
- return false;
- }
-
- $mimeType = $this->getMimeType($tempfile);
-
- if(! preg_match("/^image\/(?:jpg|jpeg|gif|png)$/i", $mimeType)){
-
- $this->debug(3, "Remote file has invalid mime type: $mimeType");
-
- @unlink($this->cachefile);
-
- touch($this->cachefile);
-
- $this->error("The remote file is not a valid image.");
- return false;
- }
-
- if($this->processImageAndWriteToCache($tempfile)){
- $this->debug(3, "Image processed succesfully. Serving from cache");
-
- return $this->serveCacheFile();
- } else {
-
- return false;
- }
- }
-
- public static function curlWrite($h, $d){
-
- fwrite(self::$curlFH, $d);
-
- self::$curlDataWritten += strlen($d);
-
- if(self::$curlDataWritten > MAX_FILE_SIZE){
- return 0;
-
- } else {
- return strlen($d);
- }
- }
-
- protected function serveCacheFile(){
-
- $this->debug(3, "Serving {$this->cachefile}");
-
- if(! is_file($this->cachefile)){
-
- $this->error("serveCacheFile called in timthumb but we couldn't find the cached file.");
-
- return false;
- }
-
- $fp = fopen($this->cachefile, 'rb');
- //如果打开失败,直接退出脚本,并记录错误信息
- if(! $fp){ return $this->error("Could not open cachefile."); }
- //设定文件指针跳过filePrependSecurityBlock值,也就是跳过安全头后开始读
- fseek($fp, strlen($this->filePrependSecurityBlock), SEEK_SET);
- //读出文件的mime类型
- $imgType = fread($fp, 3);
- //再跳过这个mime类型的值
- fseek($fp, 3, SEEK_CUR);
- //如果现在文件的指针不是在安全头后6个字符的位置,说明缓存文件可能已损坏
- if(ftell($fp) != strlen($this->filePrependSecurityBlock) + 6){
- //删除此缓存文件
- @unlink($this->cachefile);
- //记录错误并退出执行
- return $this->error("The cached image file seems to be corrupt.");
- }
- //缓存图片的实际大小应该是文件大小 - 安全头大小
- $imageDataSize = filesize($this->cachefile) - (strlen($this->filePrependSecurityBlock) + 6);
- //设置输出必要的HTTP头
- $this->sendImageHeaders($imgType, $imageDataSize);
- //输出文件指针处所有剩余数据
- $bytesSent = @fpassthru($fp);
- //关闭文件资源
- fclose($fp);
- //如果此方法执行成功,则返回真
- if($bytesSent > 0){
- return true;
- }
- //如果fpassthru不成功,则用file_get_contents读取并输出
- $content = file_get_contents ($this->cachefile);
- //如果读取成功
- if ($content != FALSE) {
- //截取掉安全头
- $content = substr($content, strlen($this->filePrependSecurityBlock) + 6);
- //输出图像
- echo $content;
- //写日志,记录读取缓存的方式
- $this->debug(3, "Served using file_get_contents and echo");
- return true;
- //读取失败的话记录错误信息并退出执行
- } else {
- $this->error("Cache file could not be loaded.");
- return false;
- }
- }
- /*此函数设置图片输出必要的http头*/
- protected function sendImageHeaders($mimeType, $dataSize){
- //补全图片的mime信息
- if(! preg_match('/^image\
- $mimeType = 'image/' . $mimeType;
- }
- //将jpg的mime类型写标准,这里不标准的原因是在验证文件安全头时追求了便利性
- if(strtolower($mimeType) == 'image/jpg'){
- $mimeType = 'image/jpeg';
- }
- //浏览器缓存失效时间
- $gmdate_expires = gmdate ('D, d M Y H:i:s', strtotime ('now +10 days')) . ' GMT';
- //文档最后被修改时间,用来让浏览器判断是否需要重新请求页面
- $gmdate_modified = gmdate ('D, d M Y H:i:s') . ' GMT';
- // 设置HTTP头
- header ('Content-Type: ' . $mimeType);
- header ('Accept-Ranges: none');
- header ('Last-Modified: ' . $gmdate_modified);
- header ('Content-Length: ' . $dataSize);
- //如果配置文件禁止浏览器缓存,则设置相应的HTTP头信息
- if(BROWSER_CACHE_DISABLE){
- $this->debug(3, "Browser cache is disabled so setting non-caching headers.");
- header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
- header("Pragma: no-cache");
- header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
- //否则按配置文件设置缓存时间
- } else {
- $this->debug(3, "Browser caching is enabled");
- header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate');
- header('Expires: ' . $gmdate_expires);
- }
- //运行成功返回真
- return true;
- }
- /*自定义的验证函数*/
- protected function securityChecks(){
- }
- /*此函数用来获取$_GET数组中的参数,并允许设置默认值*/
- protected function param($property, $default = ''){
- //如果参数存在则返回此参数
- if (isset ($_GET[$property])) {
- return $_GET[$property];
- //不存在的话返回默认值
- } else {
- return $default;
- }
- }
- /*此函数根据传入mime类型,打开图像资源*/
- protected function openImage($mimeType, $src){
- switch ($mimeType) {
- case 'image/jpeg':
- $image = imagecreatefromjpeg ($src);
- break;
-
- case 'image/png':
- $image = imagecreatefrompng ($src);
- break;
-
- case 'image/gif':
- $image = imagecreatefromgif ($src);
- break;
- //不是这三种的话,脚本退出
- default:
- $this->error("Unrecognised mimeType");
- }
- //返回图像资源
- return $image;
- }
- /*没啥说的,获取客户端IP*/
- protected function getIP(){
- $rem = @$_SERVER["REMOTE_ADDR"];
- $ff = @$_SERVER["HTTP_X_FORWARDED_FOR"];
- $ci = @$_SERVER["HTTP_CLIENT_IP"];
- if(preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)){
- if($ff){ return $ff; }
- if($ci){ return $ci; }
- return $rem;
- } else {
- if($rem){ return $rem; }
- if($ff){ return $ff; }
- if($ci){ return $ci; }
- return "UNKNOWN";
- }
- }
- /*debug运行日志函数,用来向系统日志记录操作信息*/
- protected function debug($level, $msg){
- //如果开启了debug,并且$level也就是调试级别小于等于配置文件中的值,则开始记录
- if(DEBUG_ON && $level <= DEBUG_LEVEL){
- //格式化并记录开始时间,保留小数点后6位,这个时间代表实例化类后到这个debug执行所经历的时间
- $execTime = sprintf('%.6f', microtime(true) - $this->startTime);
- //这个值代表从上次debug结束,到这次debug的用时
- $tick = sprintf('%.6f', 0);
- //如果上次debug时间存在,则用当前时间减去上次debug时间,得出差值
- if($this->lastBenchTime > 0){
- $tick = sprintf('%.6f', microtime(true) - $this->lastBenchTime);
- }
- //将时间更新
- $this->lastBenchTime = microtime(true);
- //将debug信息写到系统日志中
- error_log("TimThumb Debug line " . __LINE__ . " [$execTime : $tick]: $msg");
- }
- }
- /*此函数用来记录未知BUG*/
- protected function sanityFail($msg){
- //记录BUG信息
- return $this->error("There is a problem in the timthumb code. Message: Please report this error at <a href='http:
- }
-
- protected function getMimeType($file){
-
- $info = getimagesize($file);
-
- if(is_array($info) && $info['mime']){
- return $info['mime'];
- }
-
- return '';
- }
-
- protected function setMemoryLimit(){
-
- $inimem = ini_get('memory_limit');
-
- $inibytes = timthumb::returnBytes($inimem);
-
- $ourbytes = timthumb::returnBytes(MEMORY_LIMIT);
-
- if($inibytes < $ourbytes){
-
- ini_set ('memory_limit', MEMORY_LIMIT);
-
- $this->debug(3, "Increased memory from $inimem to " . MEMORY_LIMIT);
-
- } else {
-
- $this->debug(3, "Not adjusting memory size because the current setting is " . $inimem . " and our size of " . MEMORY_LIMIT . " is smaller.");
- }
- }
-
- protected static function returnBytes($size_str){
-
- switch (substr ($size_str, -1))
- {
- case 'M': case 'm': return (int)$size_str * 1048576;
- case 'K': case 'k': return (int)$size_str * 1024;
- case 'G': case 'g': return (int)$size_str * 1073741824;
- default: return $size_str;
- }
- }
-
- protected function getURL($url, $tempfile){
-
- $this->lastURLError = false;
-
- $url = preg_replace('/ /', '%20', $url);
-
- if(function_exists('curl_init')){
-
- $this->debug(3, "Curl is installed so using it to fetch URL.");
-
- self::$curlFH = fopen($tempfile, 'w');
-
- if(! self::$curlFH){
- $this->error("Could not open $tempfile for writing.");
- return false;
- }
-
- self::$curlDataWritten = 0;
-
- $this->debug(3, "Fetching url with curl: $url");
-
- $curl = curl_init($url);
-
- curl_setopt ($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
- curl_setopt ($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30");
- curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
- curl_setopt ($curl, CURLOPT_HEADER, 0);
- curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
-
- curl_setopt ($curl, CURLOPT_WRITEFUNCTION, 'timthumb::curlWrite');
- @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
- @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
-
- $curlResult = curl_exec($curl);
-
- fclose(self::$curlFH);
-
- $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-
- if($httpStatus == 404){
- $this->set404();
- }
-
- if($curlResult){
-
- curl_close($curl);
-
- return true;
-
- } else {
-
- $this->lastURLError = curl_error($curl);
-
- curl_close($curl);
-
- return false;
- }
-
- } else {
-
- $img = @file_get_contents ($url);
-
- if($img === false){
-
- $err = error_get_last();
-
- if(is_array($err) && $err['message']){
-
- $this->lastURLError = $err['message'];
-
- } else {
- $this->lastURLError = $err;
- }
-
- if(preg_match('/404/', $this->lastURLError)){
- $this->set404();
- }
-
- return false;
- }
-
- if(! file_put_contents($tempfile, $img)){
-
- $this->error("Could not write to $tempfile.");
- return false;
- }
-
- return true;
- }
-
- }
-
- protected function serveImg($file){
-
- $s = getimagesize($file);
-
- if(! ($s && $s['mime'])){
- return false;
- }
-
- header ('Content-Type: ' . $s['mime']);
- header ('Content-Length: ' . filesize($file) );
- header ('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
- header ("Pragma: no-cache");
-
- $bytes = @readfile($file);
- if($bytes > 0){
- return true;
- }
-
- $content = @file_get_contents ($file);
- if ($content != FALSE){
- echo $content;
- return true;
- }
-
- return false;
- }
-
- protected function set404(){
- $this->is404 = true;
- }
-
- protected function is404(){
- return $this->is404;
- }
- }