主要内容:
- 文件上传的异常处理
- 将文件从临时目录移动到正式目录
- 批量上传文件
- 分页操作
1. 文件上传的异常处理
<?php
error_reporting(E_ALL);
// $_FILES: 文件上传变量,超全局的变量数组, 保存着所有与文件上传的相关信息
printf('<pre>%s</pre>', print_r($_FILES, true));
// 自定义文件上传的异常处理类
class UploadException extends Exception
{
public function __toString()
{
return <<< UPLOAD
<table>
<tr>
<td>编号</td>
<td>信息</td>
<td>文件</td>
<td>行号</td>
</tr>
<tr>
<td>$this->code</td>
<td>$this->message</td>
<td>$this->file</td>
<td>$this->line</td>
</tr>
</table>
<style>
table {border-collapse: collapse;border:1px solid black;text-align: center;}
td {border:1px solid black;padding: 5px;}
tr:first-of-type {background-color:#eee;}
tr:last-of-type td {color: red;}
</style>
UPLOAD;
}
}
try {
// 对错误编码的判断
$errorCode = $_FILES['my_pic']['error'] ?? null;
// 使用系统常量进行判断
if ($errorCode > UPLOAD_ERR_OK) { //也就是说大于0。
switch ($errorCode) {
case UPLOAD_ERR_INI_SIZE:
throw new UploadException('上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值',1);
break;
case UPLOAD_ERR_FORM_SIZE:
throw new UploadException('上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值', 2);
break;
case UPLOAD_ERR_PARTIAL:
throw new UploadException('文件只有部分被上传', 3);
break;
case UPLOAD_ERR_NO_FILE:
throw new UploadException('没有文件被上传', 4);
break;
case UPLOAD_ERR_NO_TMP_DIR:
throw new UploadException('找不到临时文件夹', 5);
break;
case UPLOAD_ERR_CANT_WRITE:
throw new UploadException('文件写入失败', 6);
break;
default:
// 测试时建议关掉default: 避免误报影响
throw new UploadException('未知类型错误', 7);
}
}
// 判断文件类型是否正确?
$fileType = $_FILES['my_pic']['type'] ?? null;
$type = strstr($fileType, '/', true); // "image"
if (!is_null($fileType)) {
if ($type !== 'image') throw new UploadException('文件类型错误', 8);
}
// 判断上传方式是否正确?
// 临时文件名
$tmplFileName = $_FILES['my_pic']['tmp_name'] ?? null;
// 首先临时文件是存在的,并且必须是通过POST上传的
if (!($tmplFileName && is_uploaded_file($tmplFileName))) throw new UploadException('上传类型错误', 9);
} catch (UploadException $e) {
echo $e;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传的异常处理</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>单文件上传</legend>
<!-- 隐藏域设置上传文件大小,必须写到input:file之前 -->
<input type="hidden" name="MAX_FILE_SIZE" value="38000">
<input type="file" name="my_pic" id="">
<button>上传</button>
</fieldset>
</form>
</body>
</html>
2. 将文件从临时目录移动到正式目录
<?php
error_reporting(E_ALL);
// $_FILES: 文件上传变量,超全局的变量数组, 保存着所有与文件上传的相关信息
printf('<pre>%s</pre>', print_r($_FILES, true));
// 自定义文件上传的异常处理类
class UploadException extends Exception
{
public function __toString()
{
return <<< UPLOAD
<table>
<tr>
<td>编号</td>
<td>信息</td>
<td>文件</td>
<td>行号</td>
</tr>
<tr>
<td>$this->code</td>
<td>$this->message</td>
<td>$this->file</td>
<td>$this->line</td>
</tr>
</table>
<style>
table {border-collapse: collapse;border:1px solid black;text-align: center;}
td {border:1px solid black;padding: 5px;}
tr:first-of-type {background-color:#eee;}
tr:last-of-type td {color: red;}
</style>
UPLOAD;
}
}
try {
// 对错误编码的判断
$errorCode = $_FILES['my_pic']['error'] ?? null;
// 使用系统常量进行判断
if ($errorCode > UPLOAD_ERR_OK) {
switch ($errorCode) {
case UPLOAD_ERR_INI_SIZE:
throw new UploadException('上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值',1);
break;
case UPLOAD_ERR_FORM_SIZE:
throw new UploadException('上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值', 2);
break;
case UPLOAD_ERR_PARTIAL:
throw new UploadException('文件只有部分被上传', 3);
break;
case UPLOAD_ERR_NO_FILE:
throw new UploadException('没有文件被上传', 4);
break;
case UPLOAD_ERR_NO_TMP_DIR:
throw new UploadException('找不到临时文件夹', 5);
break;
case UPLOAD_ERR_CANT_WRITE:
throw new UploadException('文件写入失败', 6);
break;
default:
// 测试时建议关掉default: 避免误报影响
throw new UploadException('未知类型错误', 7);
}
}
// 判断文件类型是否正确?
$fileType = $_FILES['my_pic']['type'] ?? null;
$type = strstr($fileType, '/', true); // "image"
if (!is_null($fileType)) {
if ($type !== 'image') throw new UploadException('文件类型错误', 8);
}
// 判断上传方式是否正确?
// 临时文件名
$tmplFileName = $_FILES['my_pic']['tmp_name'] ?? null;
// 1. 首先临时文件是存在的,并且必须是通过POST上传的
if ($tmplFileName && is_uploaded_file($tmplFileName)) {
// 2. 将文件从临时目录移动到目标目录中: uploads/
// move_uploaded_file(临时文件, 目标文件名)
// 原始文件名
$originalFileName = $_FILES['my_pic']['name'] ?? null;
// echo strstr($originalFileName, '.'); //显示扩展名
// 目标文件名,用时间加盐,然后加载一个随机数,再用md5生成加密字符串作为文件名。
// 这样同一文件多次上传也不会覆盖,而是生成新的。
$destFileName = 'uploads/' . md5(time(). mt_rand(1,1000)) .strstr($originalFileName, '.');
if (move_uploaded_file($tmplFileName, $destFileName)) {
echo '<p>'. $_FILES['my_pic']['name'].': 上传成功</p>';
// 预览图片
echo "<img src='{$destFileName}' width='200'>";
}
}
} catch (UploadException $e) {
echo $e;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>将文件从临时目录移动到正式目录中</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>单文件上传</legend>
<!-- 隐藏域设置上传文件大小,必须写到input:file之前 -->
<!-- <input type="hidden" name="MAX_FILE_SIZE" value="38000"> -->
<input type="file" name="my_pic" id="">
<button>上传</button>
</fieldset>
</form>
</body>
</html>
3. 批量上传文件
<?php
error_reporting(E_ALL);
printf('<pre>%s</pre>', print_r($_FILES, true));
// 异常处理, 错误处理,这里省略...
foreach ($_FILES as $file) {
// print_r($file);
// 判断 $file['error'] 是否为0,如果为0表示上传成功
if ($file['error'] === 0) {
$destFileName = 'uploads/' . md5(time(). mt_rand(1,1000)) .strstr($file['name'], '.');
if (move_uploaded_file($file['tmp_name'], $destFileName)) {
echo '<p>'. $file['name'].': 上传成功</p>';
// 预览图片
echo "<img src='{$destFileName}' width='200'>";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多文件上传</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>批量上传文件</legend>
<!-- 隐藏域设置上传文件大小,必须写到input:file之前 -->
<!-- <input type="hidden" name="MAX_FILE_SIZE" value="38000"> -->
<input type="file" name="my_pic1" id="">
<input type="file" name="my_pic2" id="">
<input type="file" name="my_pic3" id="">
<button>上传</button>
</fieldset>
</form>
</body>
</html>
4. 分页操作
4-1. 基本知识: 分页原理
4-1-1. 术语
- 记录索引: 记录在表中的位置,从 0 开始编号(与主键的值无关)
- 偏移量: 每页显示的索引距离起始索引 0 的相对位置
- 显示数量: 每页的显示记录的条数
SELECT * FROM `users` 每页的数量 显示的偏移量
-- 每页的数量: LIMIT n
-- 显示的偏移量: 从哪个索引开始显示 OFFSET m
# 从第1页开始显示5条
SELECT * FROM `users` LIMIT 5 OFFSET 0;
-- (1-1)*5 = 0
# 第二页,从索引5开始显示
SELECT * FROM `users` LIMIT 5 OFFSET 5;
-- (2-1)*5 = 5
# 第3页,从索引10开始,即偏移量为10, 偏移量从第一条记录的索引开始计数
SELECT * FROM `users` LIMIT 5 OFFSET 10;
-- (3-1)*5 = 10
- 分页的时候,一定会提供当前的页数,还有就是每一页的显示的记录数量
- 而偏移量是动态变量的变量,随页数不同而发生变化
4-1-2. 计算偏移量
- 偏移量 = (页码 -1) * 每页的显示数量
- offset = (page - 1) * num
4-2. 操作
<?php
// 连接数据库
// PDO连接对象
$pdo = new PDO('mysql:host=localhost;dbname=phpedu', 'root', 'root');
// 设置结果默认获取方式: 关联数组
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
<?php
// 连接数据库
require 'connect.php';
// 获取分页数据, 一定要知道的二个数据
// 1. 每页显示的数量
$num = 5;
// 2. 当前页码,默认为1
$page = $_GET['p'] ?? 1;
// 3. 计算每一页的第一条记录的显示偏移量(每页展示量)
$offset = ($page - 1) * $num;
// 4. 获取分页数据
// SElECT * FROM `table_name `LIMIT n OFFSET m;
$sql = "SELECT * FROM `users` LIMIT {$num} OFFSET {$offset}";
// 简写
// $sql = "SELECT * FROM `users` LIMIT {$offset}, {$num}";
$users = $pdo->query($sql)->fetchAll();
// print_r($users);
// 计算总页数
<?php require 'demo4.php' ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据展示</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<table>
<caption>用户信息表</caption>
<thead>
<tr>
<td>id</td>
<td>name</td>
<td>email</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user):?>
<tr>
<!-- 短标签来简化变量的显示 -->
<td><?=$user['id']?></td>
<td><?php echo $user['name'] ?></td>
<td><?=$user['email']?></td>
<td><button>编辑</button><button>删除</button></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
color: #555;
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
/*表格样式*/
table {
width: 80%;
border: 1px solid;
border-collapse: collapse;
text-align: center;
}
table caption {
font-size: 1.2rem;
margin: 10px;
}
table td,
table th {
border: 1px solid;
padding: 5px;
}
table tr:hover {
background-color: #eee;
}
table thead tr:only-of-type {
background-color: lightblue;
}
table button {
width: 56px;
height: 26px;
}
table button:last-of-type {
color: red;
}
table button {
cursor: pointer;
margin: 0 3px;
}
/*分页条样式*/
body > p {
display: flex;
}
p > a {
text-decoration: none;
color: #555;
border: 1px solid;
padding: 5px 10px;
margin: 10px 2px;
}
.active {
background-color: red;
color: white;
border: 1px solid red;
}
5. 作业实战
<?php
/*
1. 实现单文件上传,要求自定义异常类来处理常见错误
2. 将demo3.php中的文件批量上传中缺失的异常处理, 文件类型处理等操作补齐
附加/选做: 封装一个文件上传类,可同时处理单文件与多文件上传
*/
error_reporting(E_ALL);
printf('<pre>%s</pre>',print_r($_FILES, true));
class UploadEcpt extends Exception
{
public function __toString()
//__toString应该是Exception中唯一可以进行访问的功能
{
return <<< UPLOAD
<table>
<tr>
<td>编号</td>
<td>信息</td>
<td>文件</td>
<td>line</td>
</tr>
<tr>
<td>$this->code</td>
<td>$this->message</td>
<td>$this->file</td>
<td>$this->line</td>
</tr>
<style>
table {border-collapse:collapse;border:1px solid yellow;
text-align:center;}
td {border: 1px solid black; padding:5px;}
tr: first-of-type {background-color:#eee;}
tr: last-of-type td {color:red;}
</style>
</table>
UPLOAD;
}
}
$errorCode = $_FILES['my_file']['error']?? null;
//下面这块的内容简化到$errorCode中。这个是学老周的方法
$fileType = $_FILES['my_file']['type']?? null;
$type = strstr($fileType, '/', true);
$tmpFileName = $_FILES['my_file']['tmp_name']?? null;
if (!is_null($fileType) && $type !== 'image') $errorCode = 8;
if (!$tmpFileName && is_uploaded_file($tmpFileName)) $errorCode = 9;
try {
if ($errorCode > 0) {
switch($errorCode){
case 1: throw new UploadEcpt("文件超过php.ini限制值",1); break;
case 2: throw new UploadEcpt("文件超过表单限定值",2); break;
case 3: throw new UploadEcpt("文件只有部分被上传",3); break;
case 4: throw new UploadEcpt("没有文件被上传",4); break;
case 5: throw new UploadEcpt("",5); break;
case 6: throw new UploadEcpt("找不到临时文件夹",6); break;
case 7: throw new UploadEcpt("文件写入失败",7); break;
case 8: throw new UploadEcpt("文件类型出错",8); break;
case 9: throw new UploadEcpt("上传方式不是POST",9); break;
default: throw new UploadEcpt("未知类型错误",77);
}
}
} catch (UploadEcpt $e){
echo $e;
}
?>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>单文件上传</legend>
<input type="hidden" name="max_file_siez" value="4000">
<input type="file" name="my_file" id="">
<button>上传</button>
</fieldset>
</form>
</body>
</html>
<?php
error_reporting(E_ALL);
printf('<pre>%s</pre>', print_r($_FILES, true));
class UploadEcpt extends Exception
{
public function __toString()
{
return <<< UPLOAD
<table>
<tr>
<td>编号</td>
<td>信息</td>
<td>文件</td>
<td>line</td>
</tr>
<tr>
<td>$this->code</td>
<td>$this->message</td>
<td>$this->file</td>
<td>$this->line</td>
</tr>
<style>
table {border-collapse:collapse;border:1px solid yellow;
text-align:center;}
td {border: 1px solid black; padding:5px;}
tr: first-of-type {background-color:#eee;}
tr: last-of-type td {color:red;}
</style>
</table>
UPLOAD;
}
}
try {
foreach ($_FILES as $FILE) {
$errorCode = $FILE['error']?? null;
$fileType = $FILE['type']?? null;
$type = strstr($fileType, '/', true);
$tmpFileName = $FILE['tmp_name']?? null;
if (!is_null($fileType) && $type !== 'image') $errorCode = 8;
if (!($tmpFileName && is_uploaded_file($tmpFileName))) $errorCode = 9;
switch($errorCode){
case 1: throw new UploadEcpt("文件超过php.ini限制值",1); break;
case 2: throw new UploadEcpt("文件超过表单限定值",2); break;
case 3: throw new UploadEcpt("文件只有部分被上传",3); break;
case 4: throw new UploadEcpt("没有文件被上传",4); break;
case 5: throw new UploadEcpt("",5); break;
case 6: throw new UploadEcpt("找不到临时文件夹",6); break;
case 7: throw new UploadEcpt("文件写入失败",7); break;
case 8: throw new UploadEcpt("文件类型出错",8); break;
case 9: throw new UploadEcpt("上传方式不是POST",9); break;
case 0:
$destFileName = 'uploads/' . md5(time(). mt_rand(1,1000)) .strstr($FILE['name'], '.');
if (move_uploaded_file($FILE['tmp_name'], $destFileName)) {
echo '<p>'. $FILE['name'].': 上传成功</p>';
echo "<img src='{$destFileName}' width='200'>";}
break;
default: throw new UploadEcpt("未知类型错误",77);
}
// 下面这段按照老周的方式赋值到了$errorCode中(line)。
// $fileType = $FILE['type']?? null;
// $type = strstr($fileType, '/', true);
// if (!is_null($fileType)) {
// if ($type !== 'image') throw new UploadEcpt('文件类型出错',8);
// }
// $tmpFileName = $FILE['tmp_name']?? null;
// if (!($tmpFileName && is_uploaded_file($tmpFileName)))
// throw new UploadEcpt('上传方式不是POST',9);
}} catch (UploadEcpt $e){
echo $e;
}
// 这段也放入到了上面的switch中。
// foreach ($_FILES as $file) {
// // print_r($file);
// // 判断 $file['error'] 是否为0,如果为0表示上传成功
// if ($file['error'] === 0) {
// $destFileName = 'uploads/' . md5(time(). mt_rand(1,1000)) .strstr($file['name'], '.');
// if (move_uploaded_file($file['tmp_name'], $destFileName)) {
// echo '<p>'. $file['name'].': 上传成功</p>';
// // 预览图片
// echo "<img src='{$destFileName}' width='200'>";
// }
// }
// }
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多文件上传</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>批量上传文件</legend>
<input type="hidden" name="MAX_FILE_SIZE" value="3800000">
<input type="file" name="my_file1" id="">
<input type="file" name="my_file2" id="">
<input type="file" name="my_file3" id="">
<button>上传</button>
</fieldset>
</form>
</body>
</html>
<?php
require 'myconnect.php';
$numPerPage = 8;
$pageNum = $_GET['p']?? 1;
$offset = ($pageNum-1) * $numPerPage;
$sql = "SELECT * FROM `shao` LIMIT {$offset}, {$numPerPage}";
$items = $pdo->query($sql)->fetchAll();
// print_r($items);
<?php require 'my1.php' ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SHAO's Items</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<table>
<caption>邵恒头条</caption>
<thead>
<tr>
<td>id</td>
<td>date</td>
<td>title</td>
<td>tags</td>
<td>action</td>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item):?>
<tr>
<!-- 短标签来简化变量的显示 -->
<td><?=$item['id']?></td>
<td><?=$item['date']?></td>
<td><?=$item['title']?></td>
<td><?=$item['label']?></td>
<td><button>编辑</button><button>删除</button></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</body>
</html>