文件上传实战案例
1. 页面
- 单文件上传页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>单个文件上传</title>
<style>
@import url("style/style.css");
</style>
</head>
<body>
<form action="doBusiness.php" method="post" enctype="multipart/form-data">
<section class="upload-box">
<div class="upload-title">
<span>请选择需要上传的文件</span>
</div>
<div class="upload-content">
<div class="upload-item">
<!-- 隐藏域限制文件大小为约256K -->
<input type="hidden" name="MAX_FILE_SIZE" value="256000">
<input type="file" name="single" id="single" />
</div>
<div class="upload-item">
<p>文件大小限制: 256KB</p>
</div>
<div class="upload-item">
<button type="submit">上传</button>
</div>
</div>
</section>
</form>
</body>
</html>
效果图:
- 多文件上传(文件域
name
属性值不同)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>多文件上传(一)</title>
<style>
@import url("style/style.css");
</style>
</head>
<body>
<form action="doBusiness.php" method="post" enctype="multipart/form-data">
<section class="upload-box">
<div class="upload-title">
<span>请选择需要上传的文件</span>
</div>
<div class="upload-content">
<!-- 隐藏域限制文件大小为约256K -->
<input type="hidden" name="MAX_FILE_SIZE" value="256000">
<div class="upload-item">
<input type="file" name="file1" id="file1" />
</div>
<div class="upload-item">
<input type="file" name="file2" id="file2" />
</div>
<div class="upload-item">
<input type="file" name="file3" id="file3" />
</div>
<div class="upload-item">
<input type="file" name="file4" id="file4" />
</div>
<div class="upload-item">
<p>文件大小限制: 256KB</p>
</div>
<div class="upload-item">
<button type="submit">上传</button>
</div>
</div>
</section>
</form>
</body>
</html>
效果图:
- 多文件上传(文件域的
name
属性值相同)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>单个文件上传(二)</title>
<style>
@import url("style/style.css");
</style>
</head>
<body>
<form action="doBusiness.php" method="post" enctype="multipart/form-data">
<section class="upload-box">
<div class="upload-title">
<span>请选择需要上传的文件</span>
</div>
<div class="upload-content">
<!-- 隐藏域限制文件大小为约256K -->
<input type="hidden" name="MAX_FILE_SIZE" value="256000">
<div class="upload-item">
<input type="file" name="file[]" />
</div>
<div class="upload-item">
<input type="file" name="file[]" />
</div>
<div class="upload-item">
<input type="file" name="file[]" />
</div>
<div class="upload-item">
<input type="file" name="file[]" />
</div>
<div class="upload-item">
<p>文件大小限制: 256KB</p>
</div>
<div class="upload-item">
<button type="submit">上传</button>
</div>
</div>
</section>
</form>
</body>
</html>
效果图:
- 文件批量上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>文件批量上传</title>
<style>
@import url("style/style.css");
</style>
</head>
<body>
<form action="doBusiness.php" method="post" enctype="multipart/form-data">
<section class="upload-box">
<div class="upload-title">
<span>请选择需要上传的文件</span>
</div>
<div class="upload-content">
<div class="upload-item">
<!-- 隐藏域限制文件大小为约256K -->
<input type="hidden" name="MAX_FILE_SIZE" value="256000">
<input type="file" name="single[]" multiple />
</div>
<div class="upload-item">
<p>文件大小限制: 256KB</p>
</div>
<div class="upload-item">
<button type="submit">上传</button>
</div>
</div>
</section>
</form>
</body>
</html>
效果图:
- 文件上传结果页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传结果</title>
<style>
@import url('style/result.css');
</style>
</head>
<body>
<?php if (!empty($success)) : ?>
<table border="1" cellspacing="0" cellpadding="10" width="800" align="center">
<caption style="font-size: 1.5rem; margin-bottom: 30px;">上传成功的文件</caption>
<thead style="background-color: lightblue;">
<tr>
<th>文件序号</th>
<th>文件名</th>
<th>缩略图</th>
<th>扩展名</th>
</tr>
</thead>
<tbody>
<?php foreach ($success as $item) : ?>
<tr>
<td><?php echo $item['filenumber'] ?></td>
<td><?php echo $item['filename'] ?></td>
<td><img src="<?php echo $item['filepath'] ?>"></td>
<td><?php echo $item['extName'] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<hr>
<?php endif; ?>
<?php if (!empty($failure)) : ?>
<table border="1" cellspacing="0" cellpadding="10" width="800" align="center">
<caption style="font-size: 1.5rem; margin-bottom: 30px;">上传失败的文件</caption>
<thead style="background:orangered">
<tr>
<th>文件序号</th>
<th>文件名</th>
<th>错误码</th>
<th>错误信息</th>
</tr>
</thead>
<tbody>
<?php foreach ($failure as $item) : ?>
<tr>
<td><?php echo $item['filenumber'] ?></td>
<td><?php echo $item['filename'] ?></td>
<td><?php echo $item['errorCode'] ?></td>
<td><?php echo $item['message'] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</body>
</html>
效果图:
2. 处理脚本
- 请求处理脚本(doBusiness.php)
<?php
// 加载文件上传处理类
require('FileUpload.php');
// 执行文件上传
$res = (new FileUpload())->doFileUpload();
// 若没有处理结果, 则表示$_FILES中的数据格式暂时无法处理
if (count($res) === 0) {
printf('<script>alert("暂不支持的文件上传形式");window.history.go(-1);</script>');
exit;
}
// printfpre($res);
// 上传成功的文件信息数组
$success = $res['success'];
// 上传失败的文件信息数组
$failure = $res['failure'];
// 加载上传结果页, 显示上传结果数据
require('upload_result.php');
- 文件上传处理类(FileUpload.php)
<?php
require('../../out.php');
class FileUpload
{
/* 文件保存路径 */
private static $fileMovePath = 'uploads/';
/* 保存处理结果的数组 */
private $res = null;
/* 合法的文件MIME类型 */
private $allowMine = ['image'];
/* 上传处理方法 */
public function doFileUpload()
{
$this->res = [];
// 批量上传的情况(三维数组,第一维数组只有一个元素)
if (COUNT($_FILES) === 1 && is_array(current($_FILES)['error'])) {
// 获取第一维数组的第一个元素
$batch_files = current($_FILES);
// 便利第二维数组中key=error的值数组
for ($index = 0; $index < count($batch_files['error']); $index++) {
// 把上传文件的信息保存为一维数组
$fileInfo = [
'name' => $batch_files['name'][$index],
'type' => $batch_files['type'][$index],
'tmp_name' => $batch_files['tmp_name'][$index],
'error' => $batch_files['error'][$index],
'size' => $batch_files['size'][$index],
];
// 执行验证和移动
// printf('开始处理第%d个文件<br/>', ($index + 1));
$this->doUpload($fileInfo, $index + 1);
// printf('第%d个文件处理完成<hr/>', ($index + 1));
}
}
// 简单处理,如果不是三维数组,则认定其为单文件上传或文件域name属性值不相同的多文件上传
else if (!is_array(current($_FILES)['error'])) {
$index = 1;
// 便利一维数组,执行验证和移动
foreach ($_FILES as $file) {
// printf('开始处理第%d个文件<br/>', $index);
$this->doUpload($file, $index);
// printf('第%d个文件处理完成<hr/>', $index);
}
}
return $this->res;
}
/**
* 执行文件上传的方法
* $file : 文件信息
* $index : 对应前端文件域的序号
*/
private function doUpload(array $file, $index)
{
// 上传出错的代码
$errorCode = $file['error'];
// 大于0的错误码为上传失败
if ($errorCode > UPLOAD_ERR_OK) {
// 异常信息
$message = '';
switch ($errorCode) {
case UPLOAD_ERR_INI_SIZE:
$message = '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值';
break;
case UPLOAD_ERR_FORM_SIZE:
$message = '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值';
break;
case UPLOAD_ERR_PARTIAL:
$message = '文件只有部分被上传';
break;
case UPLOAD_ERR_NO_FILE:
$message = '没有文件被上传';
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = '找不到临时文件夹';
break;
case UPLOAD_ERR_CANT_WRITE:
$message = '文件写入失败';
break;
default:
// 测试时建议关掉default: 避免误报影响
$message = '未知类型错误';
}
$this->res['failure'][] = [
'filename' => $file['name'] ?? '',
'errorCode' => $errorCode,
'message' => $message,
'filenumber' => $index,
];
return;
}
// 判断文件MIME类型
$fileMine = strstr($file['type'], '/', true);
if (!in_array($fileMine, $this->allowMine)) {
$this->res['failure'][] = [
'filename' => $file['name'] ?? '',
'errorCode' => 8,
'message' => '文件MIME不允许上传',
'filenumber' => $index,
];
return;
}
// 临时文件
$tmpFileName = $file['tmp_name'];
/* 非法请求, 当处理失败处理 */
if (!is_uploaded_file($tmpFileName)) {
$this->res['failure'][] = [
'filename' => $file['name'] ?? '',
'errorCode' => 9,
'message' => '不是合法的文件上传请求,劝你善良',
'filenumber' => $index,
];
return;
}
// 准备移动
// 原文件名
$oriFileName = $file['name'];
// 文件扩展名
$extName = strstr($oriFileName, '.') ?? '';
// 目标文件名(包括路径)
$targetFileName = self::$fileMovePath . md5(time() . rand(1, 1000)) . $extName;
// 执行移动
if (move_uploaded_file($tmpFileName, $targetFileName)) {
$this->res['success'][] = [
'filename' => $oriFileName,
'filepath' => $targetFileName,
'filenumber' => $index,
'extName' => $extName,
];
} else {
$this->res['failure'][] = [
'filename' => $oriFileName,
'errorCode' => 10,
'filenumber' => $index,
'message' => '移动文件失败',
];
}
}
}
学习心得
文件上传是比较过程化的流程. 而4种文件上传提交的数据, 在$_FILES
上以不同的数据结构存放. 因此只要把不同的数据结构转成相同的结构来处理, 就可以统一操作了.