logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动

PHP课程 - 第3.5课 - 使用 MySQL 数据库。JOIN 操作符。文件上传到服务器。

09/10/2025, by Ivan

在开始编写本课之前,我思考了很久该如何更好地讲解带有 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 创建的数据库最新转储文件。