阅读前请先下载项目源码,边读边看源码以加深理解和实操,
源码地址已放于文章末尾!


第3篇:“乾坤大挪移”——文件上传与列表展示的艺术
各位道友,修炼至今,我们的“云盘”法宝已初具雏形,并且有了强大的“结界”(认证系统)守护。但一个空空如也的法宝,怎能称得上是真正的云盘呢?
今天,我们要修炼的核心功法,便是这“乾坤大挪移”——将散落在你电脑各处的文件,“嗖”地一下,安全、快速、有序地挪移到我们的云盘空间里。这不仅是云盘的灵魂,也是我们整个项目最激动人心的部分!
核心探秘:服务器的“快递收发室”
想象一下,当你在网页上点击“上传”按钮时,你的文件并没有直接飞到最终的 uploads 文件夹里。恰恰相反,它经历了一次非常严谨的“快递收发流程”。
服务器里有个神秘的“临时收发室”(一个临时文件夹),所有用户上传的文件,无论三七二十一,都会先被送到这里。这个包裹上贴着一张详细的“快递单”,在PHP的世界里,这张快递单就是超全局变量 $_FILES。
$_FILES 变量就像一个信息丰富的数组,里面记录了“包裹”的一切:
['name']: 包裹的原始名称,比如我的毕业论文.docx。['type']: 包裹的类型,比如application/msword。['size']: 包裹的重量(大小),单位是字节。['tmp_name']: 最关键的! 包裹在“临时收发室”的具体位置,比如/tmp/phpA1b2C3。['error']: 快递状态码。如果是0,就代表“完好无损,已签收”;如果不是0,就说明路上出了岔子。
我们的后端代码,就像是收发室里那个眼神犀利、经验丰富的管理员。他的工作流程是:
- 检查包裹:先看
['error']是不是0,确保包裹没问题。 - 核对信息:检查包裹的大小、类型等是否符合规定(我们这个项目暂时没做严格限制,但大型项目必须有)。
- 登记入库:如果一切OK,管理员才会使用一个专用“搬运工具”——
move_uploaded_file()函数,把包裹从“临时收发室”(tmp_name),安全地搬运到我们指定的“正式仓库”(uploads/文件夹下),并给它取个正式的名字。
为什么不直接用 copy() 或 rename()?因为 move_uploaded_file() 是PHP官方指定的“搬运工”,它在搬运前会再次确认这个文件是不是真的通过HTTP POST上传的,防止有人恶意伪造文件路径,搞破坏。安全,永远是第一位的!
整个过程,就像下面这张流程图一样清晰:
flowchart TD
A[用户在浏览器选择文件] --> B(点击“上传”按钮);
B --> C{浏览器打包文件发送HTTP请求};
C --> D[服务器PHP引擎接收];
D --> E[文件被存放在临时目录<br>$_FILES变量被填充];
E --> F{index.php: 检查 $_FILES['error']};
F -- Error = 0 --> G{安全检查(可选)};
G -- 通过 --> H[move_uploaded_file() <br> 从 tmp_name 移动到 uploads/];
H --> I[上传成功!];
F -- Error != 0 --> J[上传失败!];
代码实战:“四位一体”深扒上传与展示逻辑
理论讲完了,让我们直接上“手术台”,看看 index.php 和 template.php 是如何配合完成这场“乾坤大挪移”的。
1. 核心大挪移功法 - index.php 文件上传处理
a. 文件路径定位: index.php (大约在 246行 附近)
b. 全面讲解引用:
$_FILES['files']: 我们前端上传表单中input type="file"的name就是files[],所以后端用此来接收。中括号表示它是一个文件数组,可以处理多文件上传。count(): 计算数组中的单元数目。这里用来获取用户一次上传了多少个文件。pathinfo(): 返回一个包含文件路径信息的数组,我们可以从中轻松获取文件名、扩展名等。file_exists(): 检查文件或目录是否存在。我们用它来判断是否需要重命名。move_uploaded_file(): 刚刚介绍过的,安全地将上传的文件移动到新位置。
c. 逐行注释与结构化分析:
// 文件路径: index.php
// 检查是否有文件通过POST请求上传
if (isset($_FILES['files'])) {
// 获取当前文件应该被上传到的路径
$currentPath = getCurrentPath();
$uploadedFiles = $_FILES['files'];
// 获取上传文件的总数
$totalFiles = count($uploadedFiles['name']);
$successCount = 0;
// 循环处理每一个上传的文件
for ($i = 0; $i < $totalFiles; $i++) {
$fileName = $uploadedFiles['name'][$i];
$fileTmpName = $uploadedFiles['tmp_name'][$i];
$fileError = $uploadedFiles['error'][$i];
// 步骤1:确保文件上传过程中没有错误
if ($fileError === 0) {
$filePath = $currentPath . '/' . $fileName;
// 步骤2:检查目标位置是否已存在同名文件
if (file_exists($filePath)) {
// 如果存在,则自动重命名,避免覆盖
$fileNameInfo = pathinfo($fileName);
$baseName = $fileNameInfo['filename'];
$extension = isset($fileNameInfo['extension']) ? '.' . $fileNameInfo['extension'] : '';
$counter = 1;
// 循环找到一个不重复的文件名,如 file_1.txt, file_2.txt
while (file_exists($currentPath . '/' . $baseName . '_' . $counter . $extension)) {
$counter++;
}
$fileName = $baseName . '_' . $counter . $extension;
$filePath = $currentPath . '/' . $fileName;
}
// 步骤3:使用安全的函数将文件从临时目录移动到最终目的地
if (move_uploaded_file($fileTmpName, $filePath)) {
$successCount++;
}
}
}
// ... (处理成功或失败的消息,并刷新页面)
}
d. 上下文关联:
这段代码块由 template.php 中的文件上传表单触发。当用户在弹出的上传模态框中选择文件并点击“上传”后,表单会以 enctype="multipart/form-data" 的形式提交到 index.php。index.php 执行完上述的“大挪移”功法后,会通过 header() 重定向,让页面刷新。这样,当页面再次加载时,我们接下来要讲的“文件列表展示”逻辑就会执行,用户就能立刻看到自己刚刚上传的文件了。
2. “盘点家当”的艺术 - index.php 文件列表获取
文件放进仓库了,我们得有个清单才知道里面都有啥。
a. 文件路径定位: index.php (大约在 316行 附近)
b. 全面讲解引用:
DirectoryIterator: 这是PHP SPL(标准PHP库)提供的一个非常优雅的类,用于遍历目录中的文件和文件夹。它返回的是一个对象,比传统的scandir()等函数更好用。$item->isDot():DirectoryIterator对象的一个方法,用于判断当前项是否是.或..这两个特殊目录。我们必须跳过它们。$item->isDir(): 判断当前项是否是一个文件夹。$item->getFilename(): 获取文件名。$item->getMTime(): 获取文件的最后修改时间(Unix时间戳)。$item->getSize(): 获取文件大小(字节)。
c. 逐行注释与结构化分析:
// 文件路径: index.php
// 收集文件夹和文件
$folders = [];
$files = [];
// 创建一个目录迭代器,指向当前要显示的目录
$directory = new DirectoryIterator($currentPath);
// 遍历目录中的每一项
foreach ($directory as $item) {
// 步骤1:跳过 '.' 和 '..'
if ($item->isDot()) continue;
// ... (搜索过滤逻辑) ...
$itemPath = $item->getPathname(); // 获取完整路径
// 步骤2:判断是文件夹还是文件,并分别存入不同数组
if ($item->isDir()) {
$folders[] = [
'name' => $item->getFilename(),
'path' => getRelativePath($itemPath), // 获取相对路径
'time' => $item->getMTime(),
'type' => 'folder'
];
} else {
$files[] = [
'name' => $item->getFilename(),
'path' => getRelativePath($itemPath),
'time' => $item->getMTime(),
'size' => $item->getSize(),
'type' => mime_content_type($itemPath) // 获取文件的MIME类型
];
}
}
// ... (后续对 $files 数组进行排序和分页)
d. 上下文关联:
这段代码是 index.php 的核心数据准备区之一。无论是页面首次加载,还是上传、删除、重命名文件后刷新页面,它都会被执行。它就像一个勤劳的“仓库管理员”,每次都去 uploads 目录(或其子目录)清点一遍货物,生成两份最新的清单:一份是 $folders (文件夹清单),一份是 $files (文件清单)。然后,这两份清单会被郑重地交给“装修队长” template.php 去展示。
3. “发布清单”的魔法 - template.php 列表渲染
最后一步,把清单变成用户能看懂的漂亮列表。
a. 文件路径定位: template.php (大约在 535行 附近)
b. 全面讲解引用:
foreach (...) : ... endforeach;: 这是PHP在HTML模板中进行循环的一种替代语法,比使用大括号{}更清晰、更易读。htmlspecialchars(): 将特殊字符转换为HTML实体。这是一个至关重要的安全函数,可以防止XSS(跨站脚本)攻击。比如,如果一个文件名是<script>alert('hack')</script>,不使用此函数就会直接执行脚本!date(): 格式化一个本地时间/日期。formatSize(): 这是我们在index.php中自定义的一个辅助函数,用于将字节大小转换为更易读的KB, MB, GB等。
c. 逐行注释与结构化分析:
<!-- 文件路径: template.php -->
<tbody class="bg-white divide-y divide-gray-100">
<!-- 首先,循环渲染文件夹列表 -->
<?php foreach ($folders as $folder): ?>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<!-- ... (文件夹图标和链接) ... -->
<a href="index.php?path=<?php echo urlencode($folder['path']); ?>">
<!-- 使用 htmlspecialchars 防止XSS攻击 -->
<?php echo htmlspecialchars($folder['name']); ?>
</a>
<!-- ... -->
</td>
<!-- ... (文件夹的 大小/类型/时间/操作) ... -->
</tr>
<?php endforeach; ?>
<!-- 接着,循环渲染文件列表(经过分页处理的) -->
<?php foreach ($paginatedFiles as $file): ?>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<!-- ... (文件图标和多选框) ... -->
<a href="..." target="_blank">
<?php echo htmlspecialchars($file['name']); ?>
</a>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<!-- 调用自定义函数格式化文件大小 -->
<div class="text-sm text-gray-500"><?php echo formatSize($file['size']); ?></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-500"><?php echo htmlspecialchars($file['type']); ?></div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<!-- 使用 date 函数格式化时间戳 -->
<?php echo date('Y-m-d H:i:s', $file['time']); ?>
</td>
<!-- ... (文件操作按钮) ... -->
</tr>
<?php endforeach; ?>
<!-- 如果文件夹为空,显示提示信息 -->
<?php if (empty($folders) && empty($paginatedFiles)): ?>
<!-- ... -->
<?php endif; ?>
</tbody>
d. 上下文关联:
template.php 在这里扮演了最终的“渲染引擎”角色。它接收来自 index.php 的 $folders 和 $paginatedFiles 这两个“数据包”,然后通过 foreach 循环,像生产线上的机器人一样,一行一行地构建出我们看到的HTML表格。每一个 <?php echo ...; ?> 标签,都是一个数据与模板结合的“焊接点”,最终将一个动态的、数据驱动的页面完美地呈现给用户。
总结与预告
干得漂亮!今天我们打通了整个云盘项目的“任督二脉”!我们不仅学会了如何安全地接收和存储用户上传的文件,还掌握了如何高效地遍历文件目录,并将这些信息动态地展示在前端页面上。
现在,我们的云盘已经不再是一个空壳子,它有了真正的“血肉”,可以存放和展示内容了。
但是,一个优秀的“文件管家”,光会收纳和展示还不够,还得会整理!在下一篇文章中,我们将赋予用户真正的管理权限——“万物皆可控”:玩转文件的增删改查。我们将实现创建文件夹、重命名、移动和删除等核心操作。
准备好,让你的云盘变得更加强大吧!我们下期再会!

被折叠的 条评论
为什么被折叠?



