PHP课程 - 第3.5课 - 使用 MySQL 数据库。JOIN 操作符。文件上传到服务器。
在开始编写本课之前,我思考了很久该如何更好地讲解带有 JOIN 操作符的查询。事实上,JOIN 操作符用于同时从多个表中进行查询。既然我们需要另一个表,那就先创建它。我建议创建一个用于保存文件的表,我们将在本课中通过表单上传这些文件。这样,本课就会结合两个方向——数据库操作与表单处理。
我们先从添加文件上传字段开始。为了让表单能够上传文件,需要在其参数中添加表单类型:
$content .= '<form action="' . $_SERVER['PHP_SELF'] . '" method="post" enctype="multipart/form-data">';
通过 enctype 参数,我们告诉浏览器该表单将用于文件上传。现在表单已准备好,我们来添加文件上传字段:
public function display_admin() { // 消息输入方法 $content = ''; $content .= '<form action="' . $_SERVER['PHP_SELF'] . '" method="post" enctype="multipart/form-data">'; $content .= '<label for="title">名称:</label><br />'; $content .= '<input name="title" id="title" type="text" maxlength="150" />'; $content .= '<div class="clear"></div>'; $content .= '<label for="bodytext">消息:</label><br />'; $content .= '<textarea name="bodytext" id="bodytext"></textarea>'; $content .= '<input type="file" name="filename">'; // 文件上传字段 $content .= '<div class="clear"></div>'; $content .= '<input type="submit" value="添加消息" />'; $content .= '</form>'; $content .= '<p><a href="/index.php">返回首页</a></p>'; return $content; }
通过 input type="file",我们就能上传文件。如果保存并刷新添加消息的页面,就会出现文件上传字段。
现在看看文件是如何通过 POST 请求传递的。
public function write($p) { print_r($p); // 打印表单数组 print_r($_FILES); // 打印文件数组 $sql = 'INSERT INTO Messages (title, bodytext, created) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ')'; return mysql_query($sql); }
现在上传一个文件并保存消息。所有通过表单上传的文件都可以在超级全局变量 $_FILES 中获取。我上传了一个文件,得到了如下数组:
Array ( [title] => asfasdf [bodytext] => asfasdf ) Array ( [filename] => Array ( [name] => ip.txt [type] => text/plain [tmp_name] => Y:\tmp\phpAA.tmp [error] => 0 [size] => 13 ) )
既然我们已经有了文件和数据数组,就来创建一个文件表用于保存上传的文件。首先,在网站根目录下创建 files 文件夹;如果你使用的是 Linux 系统或主机,请给该文件夹设置 777 权限,以便脚本能写入文件。
修改 buildDB() 方法中的建表语句:
$sql = "CREATE TABLE Messages ( mid int NOT NULL AUTO_INCREMENT, PRIMARY KEY(mid), title varchar(15), bodytext text, created int(11), file int(11), ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; CREATE TABLE Files ( fid int NOT NULL AUTO_INCREMENT, PRIMARY KEY(fid), filename varchar(255), filepath varchar(255), filemime varchar(255), filesize int(10), timestamp int(10), )ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;";
除了添加新表,我们还新增了一个 fid 字段,用作文件表的外键。新建的文件表包含以下字段:
fid — 表的主键,即文件编号。
filename — 文件名。
filepath — 从网站根目录起的文件路径。
filemime — 文件类型,用于识别扩展名与用途(例如文本文件是 text/plain,图片可能是 image/jpg 或 image/png)。
filesize — 文件大小(字节)。
timestamp — 文件上传时间。
接下来保存文件并将其插入数据库。可以使用 phpMyAdmin 创建所需字段。
if($_FILES["filename"]["size"] > 1024*3*1024) { echo ("文件大小超过3MB"); exit; } // 检查文件是否上传 if(is_uploaded_file($_FILES["filename"]["tmp_name"])) { // 如果上传成功,将文件从临时目录移动到目标目录 move_uploaded_file($_FILES["filename"]["tmp_name"], "/files/".$_FILES["filename"]["name"]); } else { echo("文件上传错误"); }
这样,我们就将文件保存到了 files 文件夹。文件上传成功后,向数据库的 Files 表插入一条记录:
$sql = 'INSERT INTO Files (filename, filepath, filemime, filesize, timestamp) VALUES ("'. $_FILES['filename']['name'] . '", "files/' . $_FILES['filename']['name'] . '", "'. $_FILES['filename']['type'] .'", '. $_FILES['filename']['size'] .', '. time() . ')'; mysql_query($sql);
插入文件记录后,我们还要插入消息。修改插入消息的 SQL,添加 fid 字段:
$sql = 'INSERT INTO Messages (title, bodytext, created, fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID())';
请注意函数 last_insert_id(),这是 MySQL 的函数,用于返回最后插入记录的 ID。在我们的情况下,最后插入的是文件记录,因此返回文件表中的 ID。
现在记录已成功写入数据库,接下来我们用 JOIN 操作符来输出它们。修改消息显示的 SQL,使其同时输出文件名:
public function display_public() { // 输出消息方法 $content = ''; $sql = 'SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid ORDER BY mid DESC'; $result = mysql_query($sql) or die(mysql_error()); ...
这里我使用了 LEFT JOIN。实际上,表的联合查询有多种方式,我们将在后续课程中详细讲解 JOIN 的使用。暂时记住,LEFT JOIN 会输出所有记录,包括那些没有 fid 的。
打印查询数组:
public function display_public() { // 输出消息方法 $content = ''; $sql = 'SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid ORDER BY mid DESC'; $result = mysql_query($sql) or die(mysql_error()); while($row = mysql_fetch_array($result)){ // 使用 mysql_fetch_array() 处理查询结果 print_r($row);
可以看到输出的数据既来自消息表,也来自文件表。如果没有对应文件,相应字段则为空,因此我们应检查这些字段。
if(!empty($row['filename'])){ $content .= '<p>附件:<a target="_blank" href="/'. $row['filepath'] .'">'. $row['filename'] .'</a></p>'; }
现在消息中会附带一个文件链接形式的附件。下一课我们将更详细地学习强大的 JOIN 操作符,它可以同时从多张表中输出数据。之后我会在附件中提供通过 phpMyAdmin 创建的数据库最新转储文件。