学习目标

  • 准备工作
  • 创建数据库
  • 用户登录功能
  • 显示照片列表
  • 添加照片
  • 显示照片详细内容

准备工作

  1. 复制所有的视图文件(html页面),到网站根目录中
  2. 复制字体文件,到images目录中

创建数据库

1、创建数据库 imageSystem

-- 创建数据库imageSystem
CREATE DATABASE IF NOT EXISTS `imageSystem` CHARSET utf8;

2、创建用户数据表 users

-- 创建用户数据表
CREATE TABLE IF NOT EXISTS `users` (
   `id` int not null auto_increment primary key, -- 用户ID
   `username` varchar(20), -- 用户名
   `password` char(32) -- 用户密码
)ENGINE=InnoDB;

创建结果
创建结果

3、添加一条用户数据

-- 向user表中插入一条数据,用户代码测试。
INSERT INTO `user` (username, password) VALUES ('root', '63a9f0ea7bb98050796b649e85481845');

如何获取md5加密字符串?

<?php
// 获取加密密码
echo md5('root'); // 63a9f0ea7bb98050796b649e85481845

插入数据成功
插入数据成功

4、创建相册数据表 photos

-- 创建相册数据表
CREATE TABLE IF NOT EXISTS `photos` (
   `id` int not null auto_increment primary key, -- 照片ID
   `title` varchar(20), -- 照片标题
   `imgsrc` varchar(100), -- 照片连接
   `info` text, -- 相片描述
   `viewsNum` int not null default 0, -- 浏览数
   `created` int(10), -- 发布时间
)ENGINE=InnoDB;

创建成功
创建成功

用户登录功能

1、登录表单页面 login.php

登录页面程序处理流程

  1. 判断表单是否合法提交(防止恶意爬虫攻击);
  2. 获取表单提交数据;
  3. 判断验证码是否与服务器生成的一致(对比失败跳转回登陆页面);
  4. 从数据库取出用户信息进行账号密码比对;
  5. 将用户信息保存至服务器SESSION中(对比失败跳转回登陆页面);
  6. 跳转用户窗口到首页。

隐藏域验证

在PHP中生成一个随机字符串,并且存储在服务器SESSION中,当表单提交时,和表单隐藏域中的值进行对比,不一样说明是接受表单页面的客户端与发送表单的客户端不是同一设备。

<?php
session_start(); //使用SESSION之前必须开启SESSION会话
$_SESSION['token'] = uniqid(); //产生表单验证随机字符串
?>
    
<form method="post" action="loginSave.php">
    ...
    <input type="hidden" name="token" value="<?php echo $_SESSION['token'] ?>">
    ...
</form>

2、登录程序处理页面 loginSave.php

主要功能:

  1. 获取login.php中表单数据(包括隐藏域数据);
  2. 依次完成隐藏域、验证码、账号和密码的验证。
<?php
//包含连接数据库的公共文件
require_once("./connect-mysql.php");
//开启SESSION会话
session_start();

//判断表单是否合法提交
if (isset($_POST['token']) && $_POST['token'] == $_SESSION['token']) {
    //获取表单提交数据
    $username = $_POST['username']; //用户名
    $password = md5($_POST['password']); //加密字符串
    $verify_code = $_POST['verify_code']; //验证码

    //判断验证码与服务器保存的是否一致:验证码不区分大小写
    if (strtolower($verify_code) != strtolower($_SESSION['verify_code'])) {
        echo "<h2>验证码错误!</h2>";
        header("refresh:3;url=./login.php");
        die();
    }

    //判断用户名和密码与数据库是否一致
    $sql_query = "SELECT * FROM users WHERE username='$username' and password='$password'";
    $result = mysqli_query($sql_link, $sql_query); //执行SQL语句,并返回结果集对象
    if (!mysqli_num_rows($result)) { //取回记录数:0没找到,1找到了
        echo "<h2>用户名或密码不正确!</h2>";
        header("refresh:3;url=./login.php");
        die();
    }

    // //保存用户信息到SESSION中
    $_SESSION['username'] = $username;

    // //跳转到相册首页
    header("location:./index.php");
} else {
    //直接跳转到login.php
    header("refresh:3;url=./login.php");
    echo "<h2>非法登录!5秒后跳转...</h2>";
}

3、连接数据库公共文件 connect-mysql.php

<?php
//(1)数据库配置信息
$db_host = "localhost"; //主机名
$db_username = "imageSystem"; //用户名
$db_password = "imageSystem"; //密码
$db_name = "imageSystem"; //数据库名称
$charset = "utf8"; //字符集

//(2)PHP连接MySQL服务器
if (!$sql_link = @mysqli_connect($db_host, $db_username, $db_password)) {
   echo "<h2>PHP连接MySQL服务器失败!</h2>";
   die(); //中止程序向下运行
}

//(3)选择当前数据库
if (!mysqli_select_db($sql_link, $db_name)) {
   echo "<h2>选择数据库{$db_name}失败!</h2>";
   die();
}

//(4)设置字符集
mysqli_set_charset($sql_link, $charset);

// (5)链接测试代码
// echo "<h2>链接数据库{$db_name}成功!</h2>";

4、创建验证码页面 verify-code.php

主要功能:

  1. 生成验证码;
  2. 验证码保存至服务器SESSION;
  3. 当用户登陆时服务器SESSION验证码与用户输入进行对比;
  4. 完成前端页面的验证码点击刷新功能。
<?php

/**
 * 实例:利用PHP中GD2模块产生验证码
 *  (1)创建一个具有数字和字母的数组
 *  (2)随机从数组中抽取四位有效数字或字母用于添加到画布中
 *  (3)创建一个画布,并对画布进行一些处理
 *  (4)将验证码写入画布
 *  (5)生成图片并输出
 *  (6)销毁画布
 */

// (1)创建一个具有数字和字母的数组
$randArr = array_merge(range('A', 'Z'), range(0, 9), range('a', 'z'));
shuffle($randArr); // 打乱数组
shuffle($randArr);

// (2)随机从数组中抽取四位有效数字或字母用于添加到画布中
$getNum = array_rand($randArr, 4); // 得到数组内四个随机下标
$verifCode = ''; // 定义一个变量用来接收随机验证码
foreach ($getNum as $index) { // 将获取的4位全部整合在一起
    $verifCode .= $randArr[$index];
}


// 将验证码保存到服务器SESSION中用于用户页面提交对比
session_start(); //开启SESSION会话
$_SESSION['verify_code'] = $verifCode; // 验证码保存至SESSION

// (3)创建一个画布,并对画布进行一些处理
$boardWidth = 120; // 定义画布的尺寸
$boardHeight = 40;
$fontfile = 'C:\\phpstudy_pro\\WWW\\a.com\\imageSystem\\font\\msyh.ttf'; // 定义字体路径(必须为绝对路径)

$drawingBoard = imagecreatetruecolor($boardWidth, $boardHeight); // 创建画布

$colorOfRand = imagecolorallocate($drawingBoard, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); // 产生一个随机色
imagefill($drawingBoard, 0, 0, $colorOfRand); // 填充画布随机背景色

// (4)将验证码写入画布
$colorOfRand = imagecolorallocate($drawingBoard, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); // 产生一个随机色
imagettftext($drawingBoard, 28, 0, 16, 32, $colorOfRand, $fontfile, $verifCode); // imagettftext(图像资源,字号大小,旋转角度,X坐标,Y坐标,颜色,字体文件(绝对路径),字符串)

//(5)绘制线段
for ($i = 1; $i <= 10; $i++) {
    $colorOfRand = imagecolorallocate($drawingBoard, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
    imageline($drawingBoard, mt_rand(0, $boardWidth), mt_rand(0, $boardHeight), mt_rand(0, $boardWidth), mt_rand(0, $boardHeight), $colorOfRand);
}

// (6)产生一些随机颜色像素点并填充到随机的画布位置
for ($i = 0; $i < 400; $i++) {
    $colorOfRand = imagecolorallocate($drawingBoard, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); // 产生一个随机色
    imagesetpixel($drawingBoard, mt_rand(0, $boardWidth), mt_rand(0, $boardHeight), $colorOfRand);
}

// (7)生成图片并输出
header("Content-Type:image/png");
imagepng($drawingBoard);
print_r($drawingBoard);

// (8)销毁画布
imagedestroy($drawingBoard);

5、登录页面添加验证码

疑问:如何才能实现前端点击图片即可刷新验证码?利用JS实现:给图片添加点击事件,并产生一个随机地址栏参数(防止有的浏览器因地址不变而不进行刷新,Chrome无本问题)。

<img style="float:left;margin-left:10px;cursor: pointer;height:22px;" src="./verify-code.php" onClick="this.src='./verify-code.php?'+Math.random()"> 

点击验证码刷新
点击验证码刷新

显示照片列表

1、读取照片数据 index.php

参考代码:「PHP」分页输出数据库文章

<?php
//包含连接数据库的公共文件
require_once("./connect-mysql.php");

//开启SESSION会话
session_start();
//判断用户是否登录
if (empty($_SESSION['username'])) {
   //如果用户没有登录,则直接跳转到login.php
   header("location:./login.php");
   die();
}

//每页显示多少条
$pagesize = 16;
//获取当前页码,并计算开始行号
$page = isset($_GET['page']) ? $_GET['page'] : 1;
$startrow = ($page - 1) * $pagesize;
//获取总记录数和计算总页数
$sql_query = "SELECT * FROM photos";
$result = mysqli_query($sql_link, $sql_query);
$records = mysqli_num_rows($result);
$pages = ceil($records / $pagesize); // 计算总页数

//构建查询的分页的SQL语句
$sql_query = "SELECT * FROM photos ORDER BY id DESC LIMIT {$startrow},{$pagesize}";
$result = mysqli_query($sql_link, $sql_query);
$arrs = mysqli_fetch_all($result, MYSQLI_ASSOC);
?>
<!DOCTYPE html>
<html>

<head>
   <meta charset="utf-8">
   <title>相册首页</title>
   <style type="text/css">
       /*全局样式*/
       body,
       ul,
       li,
       h2,
       a {
           margin: 0px;
           padding: 0px
       }

       body {
           font-size: 14px;
           color: #444;
           background-color: #00343f;
       }

       ul,
       li {
           list-style: none;
       }

       a {
           text-decoration: none;
           color: #444;
       }

       a:hover {
           color: red;
       }

       /*局部样式*/
       .box {
           width: 1000px;
           margin: 0px auto;
           background-color: white;
       }

       .title {
           text-align: center;
           padding: 10px 0px;
           border-bottom: 2px solid #444;
           background-color: #d0e9ff;
       }

       .title h2 {
           font-size: 36px;
           padding: 10px;
       }

       .photos {
           padding: 15px;
       }

       .photos li {
           float: left;
           width: 290px;
           padding: 8px 5px;
           margin: 10px;
           text-align: center;
           border: 1px solid red;
       }

       .photos img {
           width: 280px;
           height: 160px;
       }

       .pagelist {
           height: 40px;
           line-height: 40px;
           text-align: center;
       }

       .pagelist a {
           padding: 8px 15px;
           margin: 0px 3px;
           border: 1px solid #ccc;
       }

       .pagelist a:hover {
           border: 1px solid #0000ff;
       }

       .pagelist .current {
           padding: 8px 15px;
       }
   </style>
</head>

<body>
   <div class="box">
       <!--title-->
       <div class="title">
           <h2>我的相册</h2>
           <a href="./upload.php">上传照片</a>
           共有个<font color='red'><?php echo $records ?></font>照片
       </div>
       <!--//title-->
       <!--photos-->
       <div class="photos">
           <ul>
               <?php
               //循环二维数组
               foreach ($arrs as $arr) {
               ?>
                   <li>
                       <a href="./detail.php?id=<?php echo $arr['id'] ?>"><img src="<?php echo $arr['imgsrc'] ?>"></a>
                       <a href="./detail.php?id=<?php echo $arr['id'] ?>"><?php echo $arr['title'] ?></a>
                   </li>
               <?php } ?>
           </ul>
           <div style="clear:both"></div>
           <div class="pagelist">
               <?php
               //循环起点和终点
               $start = $page - 5;
               $end   = $page + 4;
               //如果当前页<=6时
               if ($page <= 6) {
                   $start = 1;
                   $end   = 10;
               }
               //如果$page>=$pages-4
               if ($page >= $pages - 4) {
                   $start = $pages - 9;
                   $end   = $pages;
               }
               //如果$pages<10
               if ($pages < 10) {
                   $start = 1;
                   $end   = $pages;
               }
               //循环输出所有页码
               for ($i = $start; $i <= $end; $i++) {
                   //当前页不加链接
                   if ($page == $i) {
                       echo "<span class='current'>$i</span>";
                   } else {
                       echo "<a href='./index.php?page=$i'>$i</a>";
                   }
               }
               ?>
           </div>
       </div>
       <!--//photos-->
   </div>
   <!--//box-->
</body>

</html>

2、数据分页

添加照片

1、制作添加相册的表单 upload.php

注意点:

  1. 文件表单的提交方式中必须加上特定的方式;
<form method="post" action="uploadSave.php" enctype="multipart/form-data">
    ...
</form>
  1. 由于涉及文件上传,所以该页面提交时也要增加隐藏域验证,防止恶意攻击;
  2. 判断用户是否登录,没有登陆是不能上传文件的,直接跳转到登陆页面(login.php)可以根据$_SESSION['username']的值是否为空来判断是否登录。
<?php
//开启SESSION会话
session_start();
//判断用户是否登录
if (empty($_SESSION['username'])) {
    //如果用户没有登录,则直接跳转到login.php
    header("location:./login.php");
    die();
}
//产生表单验证随机字符串
$_SESSION['token'] = uniqid();
?>
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>上传照片</title>
    <style type="text/css">
        /*全局样式*/
        body,
        h2,
        form,
        a {
            margin: 0px;
            padding: 0px
        }

        body {
            font-size: 14px;
            color: #444;
            background-color: #00343f;
        }

        a {
            text-decoration: none;
            color: #444;
        }

        a:hover {
            color: red;
        }

        /*局部样式*/
        .box {
            width: 1000px;
            margin: 0px auto;
            background-color: white;
        }

        .title {
            text-align: center;
            padding: 10px 0px;
            border-bottom: 2px solid #444;
            background-color: #d0e9ff;
        }

        .title h2 {
            font-size: 36px;
            padding: 10px;
        }

        form {
            padding: 30px;
            height: 400px;
        }

        form td {
            padding: 8px;
        }
    </style>
</head>

<body>
    <div class="box">
        <!--title-->
        <div class="title">
            <h2>上传照片</h2>
            <a href="./index.php">返回首页</a>
        </div>
        <!--//title-->
        <!--form-->
        <form method="post" action="uploadSave.php" enctype="multipart/form-data">
            <table align="center" width="600">
                <tr>
                    <td width="100" align="right">照片标题:</td>
                    <td><input type="text" name="title" size="60"></td>
                </tr>
                <tr>
                    <td align="right">上传照片:</td>
                    <td><input type="file" name="uploadFile"></td>
                </tr>
                <tr>
                    <td align="right">照片描述:</td>
                    <td><textarea name="info" cols="45" rows="5"></textarea></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input type="submit" value="提交">
                        <!-- 隐藏域验证 -->
                        <input type="hidden" name="token" value="<?php echo $_SESSION['token'] ?>">
                        <input type="reset" value="重置">
                    </td>
                </tr>
            </table>
        </form>
        <!--//form-->
    </div>
    <!--//box-->
</body>

</html>

2、表单处理程序 uploadSave.php

参考代码:「PHP」文件上传

注意点:判断文件类型不能只看文件后缀,PHP文件后缀改为jpg后上传服务器是十分危险的!!!

/**
 * 判断上传的文件是否为图片
 * 只根据文件后缀名判断文件时很危险的!!!
 * php文件改后缀为jpg后即可上传到服务器,这是十分十分十分危险的!!!
 * 此种方法不安全!!!
 */
$imgTypeArr = ['jpg', 'webp', 'gif', 'png']; // 定义一个上传文件的所有类型数组
$imgTypeStr = implode(',', $imgTypeArr); // 将文件类型数组合并为一个字符串用于打印提示信息
$imgName = $upload_file['name']; // 获取上传文件的文件名
$uploadFileSuffix = pathinfo($imgName, PATHINFO_EXTENSION); // 获取上传文件的文件后缀
if (!in_array($uploadFileSuffix, $imgTypeArr)) {
    header("refresh:3;url=./login.php");
    die("<h2>上传的图片格式必须为:" . $imgTypeStr . "中的一种!!!</h2>");
}

主体程序:

<?php
//包含连接数据库的公共文件
require_once("./connect-mysql.php");

//开启SESSION会话
session_start();

//判断用户是否登录
if (empty($_SESSION['username'])) {
    //如果用户没有登录,则直接跳转到login.php
    header("location:./login.php");
    die();
}

//判断表单的来源是否合法
if (isset($_POST['token']) && $_POST['token'] == $_SESSION['token']) {
    //**********************上传图片*******************************
    //(1)判断上传图片是否有错误发生
    $upload_file =  $_FILES['uploadFile']; // 对文件数据减维处理
    if ($upload_file['error'] != 0) {
        echo "<h2>上传图片有错误发生!错误码:" . $upload_file['error'] . "</h2>";
        header("refresh:3;url=./upload.php");
        die();
    }

    // 判断文件大小是否超过10MB = (10 * 1024 * 1024)字节
    if ($upload_file['size'] > 10 * 1024 * 1024) {
        header("refresh:3;url=./login.php");
        die("<h2>文件大小超过限制!请控制在2MB以内!</h2>");
    }
    
    //(2)判断上传文件内容类型是不是图片
    $arr1 = array("image/jpeg", "image/png", "image/gif");
    //创建finfo的资源:获取文件内容类型,与扩展名无关(使用前需要在php.ini开启extension=fileinfo)
    $finfo = finfo_open(FILEINFO_MIME_TYPE); // 创建一个资源,并返回文件类型
    //获取文件内容的原始类型,不会随着扩展名改名而改变,这个变量不能打印出来,不知道为啥???
    $mime = finfo_file($finfo,  $upload_file['tmp_name']);

    if (!in_array($mime, $arr1)) {
        echo "<h2>上传的必须是图像!</h2>";
        header("refresh:3;url=./upload.php");
        die();
    }

    //(3)判断上传的文件扩展名是不是图片
    $arr2 = array("jpg", "gif", "png");
    $ext  = pathinfo($upload_file['name'], PATHINFO_EXTENSION); //文件扩展名
    if (!in_array($ext, $arr2)) {
        echo "<h2>上传的必须是图像!</h2>";
        header("refresh:3;url=./upload.php");
        die();
    }

    //(4)移动图片到 images目录中
    $tmp_name =  $upload_file['tmp_name'];
    $save_path = "./images/" . uniqid() . "." . $ext;
    move_uploaded_file($tmp_name, $save_path);

    //***********************将表单提交数据保存到数据库****************************
    //(1)获取表单提交数据
    $title    = $_POST['title'];
    $info    = $_POST['info'];
    $imgsrc = $save_path; //将图片路径保存到数据库
    $created_time = time();

    //(2)判断记录是否添加成功
    $sql_query = "INSERT INTO photos VALUES(null,'$title','$imgsrc','$info',0,$created_time)";
    if (mysqli_query($sql_link, $sql_query)) {
        echo "<h2>上传照片成功!</h2>";
        header("refresh:3;url=./index.php");
        die();
    }
} else {
    //直接跳转到index.php页面
    header("location:./index.php");
}

显示照片详细信息detail.php

<?php
//包含连接数据库的公共文件
require_once("./connect-mysql.php");

//开启SESSION会话
session_start();
//判断用户是否登录
if (empty($_SESSION['username'])) {
    //如果用户没有登录,则直接跳转到login.php
    header("location:./login.php");
    die();
}

//获取地址栏传递的id
$id = $_GET['id'];
//更新访问量
$sql_query = "UPDATE photos SET viewsNum=viewsNum+1 WHERE id=$id";
mysqli_query($sql_link, $sql_query);

//构建查询的SQL语句
$sql_query = "SELECT * FROM photos WHERE id=$id";
//执行SQL语句,返回结果集对象
$result = mysqli_query($sql_link, $sql_query);
//获取一行数据
$arr = mysqli_fetch_assoc($result);
?>
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>照片详细信息</title>
    <style type="text/css">
        /*全局样式*/
        body,
        ul,
        li,
        h2,
        a {
            margin: 0px;
            padding: 0px
        }

        body {
            font-size: 14px;
            color: #444;
            background-color: #00343f;
        }

        ul,
        li {
            list-style: none;
        }

        a {
            text-decoration: none;
            color: #444;
        }

        a:hover {
            color: red;
        }

        /*局部样式*/
        .box {
            width: 1000px;
            margin: 0px auto;
            background-color: white;
        }

        .title {
            text-align: center;
            padding: 10px 0px;
            border-bottom: 2px solid #444;
            background-color: #d0e9ff;
        }

        .title h2 {
            font-size: 36px;
            padding: 10px;
        }

        .detail {
            padding: 15px 100px;
        }

        .detail div {
            text-align: center;
        }

        .detail img {
            width: 640px;
        }

        .detail p {
            font-size: 16px;
            text-indent: 36px;
            font-family: 微软雅黑;
            line-height: 28px;
        }
    </style>
</head>

<body>
    <div class="box">
        <!--title-->
        <div class="title">
            <h2><?php echo $arr['title'] ?></h2>
            访问<font color=red><?php echo $arr['viewsNum'] ?></font>次,
            发布时间 <font color=red><?php echo date("Y-m-d H:i:s", $arr['created']) ?></font>,
            <a href="./index.php">返回首页</a>
        </div>
        <!--//title-->
        <!--photos-->
        <div class="detail">
            <div class="photo"><img align="center" src="<?php echo $arr['imgsrc'] ?>"></div>
            <p><?php echo $arr['info'] ?></p>
        </div>
        <!--//photos-->
    </div>
    <!--//box-->
</body>

</html>