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 基础与图像处理原理。