一、效果圖
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)) {