PHP课程 - 第4课 - 图像处理与GD2库的使用
在前几课中,我们已经学习了如何编写数据库查询,因此这节课我们将少关注“怎么写查询”,而更多地进行实际练习。同时,我们会把查询编写与其他 PHP 功能结合起来,从图像处理开始。在之前的课程中,我们已经实现了文件上传,并且数据库中已有一个 Files 表来存储上传的文件。这次,我们将把图片也上传到同一个表中。但首先,需要在内容创建表单中添加一个上传图片的字段。
$content .= '<label for="bodytext">消息:</label><br />'; $content .= '<textarea name="bodytext" id="bodytext"></textarea><br />'; $content .= '<label for="bodytext">附加文件:</label><br /><input type="file" name="filename"><br />'; $content .= '<label for="bodytext">附加图片:</label><br /><input type="file" name="image">';
接下来我们要编写文件处理逻辑,但首先需要在 Messages 表中添加一列 image_fid
,用于存储图片的 fid。图片的其他信息依然会存储在 Files 表中,就像普通文件一样。image_fid
类型为 INT(11)
,默认值为 NULL
。现在我们来修改 write()
方法,添加上传处理逻辑:
if($_FILES["image"]["size"] > 1024*3*1024){ echo ("文件大小超过3MB"); exit; } if(is_uploaded_file($_FILES["image"]["tmp_name"])){ // 检查文件是否上传成功 move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]); } else { echo("文件上传出错"); } $sql = 'INSERT INTO Files (filename, filepath, filemime, filesize, timestamp) VALUES ("'. $_FILES['image']['name'] . '", "files/' . $_FILES['image']['name'] . '", "'. $_FILES['image']['type'] .'", '. $_FILES['image']['size'] .', '. time() . ')'; mysql_query($sql);
这个处理逻辑与普通文件上传几乎没有区别。在后续课程中,我们将学习如何检查文件类型,以防止有人伪装成图片上传 PDF 等非图像文件。
现在我们可以上传两个文件,其中一个是图片。打印 $_POST
和 $_FILES
后的结构如下:
Array ( [title] => test [bodytext] => asdfasf ) Array ( [filename] => Array ( [name] => ip.txt [type] => text/plain [tmp_name] => Y:\tmp\php2C5.tmp [error] => 0 [size] => 13 ) [image] => Array ( [name] => Coat_of_arms_of_Vladimiri_Oblast.png [type] => image/png [tmp_name] => Y:\tmp\php2C6.tmp [error] => 0 [size] => 393743 ) )
接着我们要修改插入消息的 SQL:
$sql = 'INSERT INTO Messages (title, bodytext, created, fid, image_fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ', LAST_INSERT_ID()-1, LAST_INSERT_ID())';
这里使用 LAST_INSERT_ID()
来获取刚插入的文件 ID。由于普通文件先于图片插入,因此我们减去 1 来获取文件的 ID。
图片插入数据库后,我们可以显示这些图片。为此,我们将编写一个新的类 simpleImage 来处理图片。新建文件 class/simpleImage.php
并写入以下内容:
<?php class simpleCMS { } ?>
我们已经创建了一个用于图像处理的类,接下来将为它添加方法。但首先,来了解一下 PHP 中的类属性。
PHP 5 中的类属性
在 PHP4 中,类属性通过 var
定义。而在 PHP5 中,引入了更多控制属性可见性的关键字:public
、protected
和 private
。
Public
public
表示属性是公开的,可以在对象外部访问。例如:
<?php $obj = new simpleCMS(); $obj->db = 'newDB'; ?>
由于属性定义为 public
,我们可以在对象外直接修改它的值。
Protected
protected
属性只能在类的内部或其子类中访问。如果我们将 $db
定义为 protected
并尝试外部访问,将会报错:
simpleCMS.php ... protected $db = 'testDB'; ... index.php ... $obj = new simpleCMS(); $obj->db = 'newDB'; ...
现在我们添加一个受保护的变量 $path
,用于存储图像路径。我们将通过类构造函数传入这个路径。
类的构造函数
构造函数用于初始化对象的默认状态。我们会用它来设置图像路径。构造函数名以两个下划线开头:
class simpleCMS { protected $path; public function __construct($image) { $this->path = $image; } }
现在我们通过构造函数将文件路径赋值给受保护属性 $path
。接着,我们修改 public_display
方法以输出图片信息:
if(!empty($row['image_fid'])){ $sql = "SELECT * FROM Files WHERE fid=".$row['image_fid']; $image_query = mysql_query($sql) or die(mysql_error()); $image = mysql_fetch_array($image_query); $content .= '<div class="image">'; $image = new simpleImage($image['filepath']); $content .= $image->scale(100,100); $content .= '</div>'; unset($image); }
这样我们就可以从数据库中获取图片路径。现在来编写缩放(scale)与裁剪(scale_and_crop)方法。
public function scale($width,$height){ } public function scale_and_crop($width,$height){ }
缩放图像(scale)
public function scale($width, $height){ $path = $this->path; $filepath = explode('/', $this->path); $filename = end($filepath); $filename = substr($filename, 0, -4); $filename = str_replace(' ', '_', $filename); $extenstion = substr($this->path, -4); switch ($extenstion){ case '.png': $srcImage = ImageCreateFromPNG($path); break; case '.jpg': $srcImage = ImageCreateFromJPEG($path); break; case '.gif': $srcImage = ImageCreateFromGIF($path); break; default: $srcImage = ImageCreateFromGIF($path); } $srcWidth = ImageSX($srcImage); $srcHeight = ImageSY($srcImage); $ratioWidth = $srcWidth/$width; $ratioHeight = $srcHeight/$height; if($ratioWidth < $ratioHeight){ $destWidth = $srcWidth/$ratioHeight; $destHeight = $height; } else { $destWidth = $width; $destHeight = $srcHeight/$ratioWidth; } $destImage = imagecreate($destWidth, $destHeight); ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight); imagejpeg($destImage, 'files/presets/'.$width.'_'.$height.'_'.$filename.'.jpg', 100); $newImagePath = 'files/presets/'.$width.'_'.$height.'_'.$filename.'.jpg'; ImageDestroy($srcImage); ImageDestroy($destImage); return '<img src="'.$newImagePath.'" width="'.$destWidth.'" height="'.$destHeight.'" />'; }
生成的缩略图将保存在 files/presets
文件夹中,请确保该文件夹存在并设置为 777
权限。
缩放并裁剪图像(scale_and_crop)
public function scale_and_crop($width, $height){ $path = $this->path; $filepath = explode('/', $this->path); $filename = end($filepath); $filename = substr($filename, 0, -4); $filename = str_replace(' ', '_', $filename); $extenstion = substr($this->path, -4); switch ($extenstion){ case '.png': $srcImage = ImageCreateFromPNG($path); break; case '.jpg': $srcImage = ImageCreateFromJPEG($path); break; case '.gif': $srcImage = ImageCreateFromGIF($path); break; default: $srcImage = ImageCreateFromGIF($path); } $srcWidth = ImageSX($srcImage); $srcHeight = ImageSY($srcImage); $ratioSource = $srcWidth / $srcHeight; $ratioNew = $width / $height; if($ratioSource < $ratioNew){ $newWidth = $srcWidth; $newHeight = $srcHeight / $ratioNew * $ratioSource; } else { $newWidth = $srcWidth / $ratioSource * $ratioNew; $newHeight = $srcHeight; } $destImage = imagecreate($width, $height); imagecopyresampled($destImage, $srcImage, 0, 0, 0, 0, $width, $height, $newWidth, $newHeight); imagejpeg($destImage, 'files/presets/'.$width.'_'.$height.'_'.$filename.'.jpg', 100); $newImagePath = 'files/presets/'.$width.'_'.$height.'_'.$filename.'.jpg'; ImageDestroy($srcImage); ImageDestroy($destImage); return '<img src="'.$newImagePath.'" />'; }
更新 display_public()
方法:
$content .= '<div class="image">'; $image = new simpleImage($image['filepath']); $content .= $image->scale_and_crop(100,100); $content .= '</div>';
这样,图像将被缩放并裁剪为方形比例。
需要注意的是,这些图片都是“即时生成”的。由于生成图像需要一定时间,通常会将缩略图路径保存到数据库中,以避免重复生成。在本课程中,我们不会深入讲解这一优化,因为接下来的 CMS 开发课程将基于 Zend Framework,而当前的 PHP 课程旨在帮助你掌握 PHP 基础与图像处理原理。