一、效果图
1.上传文件
2.压缩包文件
3.itemno1文件
二层结构
或
三层结构
4.上传到系统路径\ItemNo
5.更名后的itemno1文件(命名:当天日期+六位随机数)
二、普通实现
1、内容介绍
含有两种结构
- 二层结构:zip->料号文件夹->料号文件
- 三层结构:zip->总文件夹->子文件夹->料号文件
(1)功能概述
- 允许用户批量上传包含产品文件的ZIP压缩包
- 自动解压ZIP文件并验证其结构
- 将文件按照料号(ItemNo)分类存储
- 为每个文件生成唯一的文件名
- 将文件信息记录到数据库
(2)主要实现步骤
a. 前端部分
- 创建一个表单,允许用户选择多个ZIP文件上传
- 表单使用
multipart/form-data
编码类型,支持文件上传 - 限制文件类型为
.zip
格式
b. 上传处理流程
文件验证:
- 检查上传的文件是否为ZIP格式
- 检查文件是否上传成功
ZIP文件处理:
- 创建临时解压目录
- 验证ZIP文件内部结构是否符合要求
- 解压ZIP文件到临时目录
文件结构分析:
- 递归查找一级子目录(料号文件夹)
- 确保结构为:ZIP -> 一级子目录 -> 文件
文件处理:
- 为每个文件生成唯一文件名(格式:YYYYMMDD_六位随机数.扩展名)
- 将文件移动到目标目录(按料号分类)
- 重命名文件以避免冲突
数据库操作:
- 检查料号是否存在,不存在则创建
- 更新料号的最后修改时间和修改人
- 记录文件信息到数据库表
清理工作:
- 删除临时解压目录
- 显示上传结果信息
c. 辅助功能
唯一文件名生成:
- 使用日期前缀加随机数确保文件名唯一
- 检查目标目录避免重复
递归删除目录:
- 安全删除临时解压目录及其所有内容
2、代码实现
<?php
include('includes/session.inc');
$Title = _('产品文件整批上传');
$ViewTopic = '产品文件整批上传';
$BookMark = '产品文件整批上传';
include('includes/header.inc');
include('includes/SQL_CommonFunctions.inc');
require_once 'upload.class.php';
echo '<p class="page_title_text"><img src="' . $RootPath . '/css/' . $Theme . '/images/magnifier.png" title="' . _('Search') . '" alt="" />' . ' ' . _('产品文件整批上传') . '</p>';
?>
<form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'UTF-8'); ?>" method='post' enctype='multipart/form-data'><input type='hidden' name='FormID' value="<?php echo $_SESSION['FormID']; ?>"><input type='file' name='folderArchive[]' accept='.zip' multiple><input type='submit' value='上传' name='update'>
</form>
<!-- 三层结构: zip->文件夹->料号文件夹->料号文件 -->
<?php
//指定路径
$target_dir = 'ItemNo/';
// 临时解压目录( 新建后会被删除 )
$unpack_dir = 'ItemNo_Updates/';
//开始上传
if (isset($_POST['update'])) {// 判断是否有上传的文件if (isset($_FILES['folderArchive']) && !empty($_FILES['folderArchive']['name'])) {//设定一个变量,值为上传的文件信息$zip_files = $_FILES['folderArchive'];//对上传的文件的name值( 例如test.zip )进行遍历foreach ($zip_files['name'] as $key => $file_name) {//检验文件是否是zip文件// pathinfo() 函数用于解析路径信息:PATHINFO_EXTENSION参数,表明它会返回文件路径中的扩展名部分(如zip、txt 等)// 将 pathinfo() 返回的扩展名转换为小写形式,if (strtolower(pathinfo($file_name, PATHINFO_EXTENSION)) !== 'zip') {// sprintf 函数:格式化字符串输出的函数prnMsg(sprintf(_('文件 "%s" 不是ZIP格式,请上传ZIP文件.'), $file_name), 'error');// 如果不是zip文件就,跳过此文件( 将不再做上传等操作 ),继续执行后面的上传文件continue;}//判断每一条数据的error值:看是否上传成功( UPLOAD_ERR_OK表示长传成功 )if ($zip_files['error'][$key] === UPLOAD_ERR_OK) {//取出该条数据的tmp_name值( 获取上传文件的临时存储路径 )$temp_file = $zip_files['tmp_name'][$key];// 先创建并打开 ZipArchive 对象,创建一个新的ZipArchive对象(ZipArchive用于读取、创建、更新和提取ZIP格式的压缩文件)$zip = new ZipArchive;//打开压缩文件if ($zip->open($temp_file) === TRUE) {//创建临时解压目录mkdir($unpack_dir, 0755, true);// 验证ZIP文件结构//设置一个变量用于判断结构是否合规$structure_valid = true;//遍历ZipArchive对象(通过 $zip 引用)中的所有文件.numFiles是压缩包内的文件数量for ($i = 0; $i < $zip->numFiles && $structure_valid; $i++) {// 获取指定索引 $i 处的ZIP压缩包内文件的名称$filename = $zip->getNameIndex($i);//将返回一个数组,数组中的每个元素对应于字符串中两个斜杠(/)之间的部分$path_parts = explode('/', $filename);// 检查文件路径分隔符个数,必须是两层结构if (count($path_parts) != 2 && count($path_parts) != 3) {//如果不是两层结构,结构不正确$structure_valid = false;prnMsg(sprintf(_('文件 "%s" 的内部结构不符合要求.'), $file_name), 'error');// prnMsg( sprintf( _( '文件 "%s" 的内部结构不符合要求(必须是 ZIP -> (一级目录) -> 文件 的结构,至少要有一级子目录).' ), $file_name ), 'error' );//跳出循环break;}}//如果结构正确if ($structure_valid) {// 解压缩文件$zip->extractTo($unpack_dir);// 关闭已经打开的 ZipArchive 对象的( 与open对应 )$zip->close();// 寻找解压后的一级子目录(不论外部文件夹是否存在)$subDirs = [];// 定义递归函数,找出所有一级子目录function findSubDirs($dir, &$subDirs){foreach (glob($dir . '/*', GLOB_ONLYDIR) as $subDir) {// 如果当前目录下还有子目录且子目录下包含文件,则认定为一级子目录if (count(glob($subDir . '/*')) > 0 && count(glob($subDir . '/*', GLOB_ONLYDIR)) == 0) {$subDirs[] = $subDir;} else {// 继续递归寻找下一级子目录findSubDirs($subDir, $subDirs);}}}foreach (glob($unpack_dir . '/*', GLOB_ONLYDIR) as $possibleOuterDir) {// 直接处理一级子目录(ZIP -> 一级子目录 -> 文件结构)if (count(glob($possibleOuterDir . '/*', GLOB_ONLYDIR)) == 0) {$subDirs[] = $possibleOuterDir;} else {// 递归寻找可能的外部文件夹下的一级子目录findSubDirs($possibleOuterDir, $subDirs);}}//遍历每个文件夹的文件foreach ($subDirs as $subDir) {// 获取料号名称( 文件夹名称 )$itemNo = basename($subDir);// 获取子文件夹中的所有文件$temp_sub_dir_files = glob($subDir . '/*');//循环子文件中的文件foreach ($temp_sub_dir_files as $file) {// 如果 $file 是一个目录,跳过if (is_dir($file)) {continue;}//验证文件是否已在目标目录中存在//获取文件名称$fileName = basename($file);// 获取目标目录下所有文件和目录的名字$existingFiles = scandir($target_dir);// 如果文件不在目标目录现存文件列表中,则是新上传的文件if (!in_array($fileName, $existingFiles)) {