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


第5篇:“一键打包带走!”——批量操作与下载功能的实现
各位“效率大师”,欢迎回来!
经过前面的修炼,我们的云盘已经掌握了“增删改查”四大神通,成为了一个合格的“文件管家”。但老实说,如果让你整理上百个文件,一个一个去点删除,是不是想死的心都有了?这种重复性的“体力活”,简直是对我们程序员优雅之道的侮辱!
真正的效率,从来都不是单点操作,而是批量处理。今天,我们就来给云盘装上“效率引擎”,实现多文件选择、批量删除、批量移动,以及最酷炫的——一键打包成ZIP下载!
核心探秘:从“单兵作战”到“集团军行动”
实现批量操作的思路,本质上是从“一对一”到“一对多”的转变。
- 前端(侦察兵):首先,我们需要在前端界面上,给用户一个能够同时“标记”多个目标的工具——多选框(Checkbox)。侦察兵(JavaScript)需要实时监控战场,知道哪些文件被选中了,并将这份“目标清单”上报给指挥部。
- 后端(指挥部):指挥部(
index.php)不再是接收一个文件名,而是接收一个文件名数组。然后,指挥官(PHP代码)会发布一道循环指令,让士兵(unlink,rename等函数)对清单上的每一个目标执行相同的操作。
而对于“打包下载”这个特殊任务,指挥部还会请来一位“打包大师”——PHP内置的 ZipArchive 类。
ZipArchive 大师的工作流程:
- 领任务:接收到需要打包的文件列表和指定的压缩包名称。
- 建空包:
$zip->open('archive.zip', ZipArchive::CREATE),先创建一个空的ZIP压缩包。 - 装东西:
$zip->addFile($filePath, $fileNameInZip),循环遍历文件列表,把一个个真实的文件“装”进ZIP包里,并可以给它们在压缩包内起个新名字。 - 封包:
$zip->close(),把包装好,封上口。 - 发快递:将打包好的ZIP文件通过HTTP响应发送给用户浏览器,并附上特殊的
header头信息,告诉浏览器:“嘿,这不是一个网页,这是一个需要下载的文件!” - 打扫战场:用户下载完成后,把服务器上临时生成的这个ZIP包删掉 (
unlink),不留痕跡。
这个流程,就像一个全自动的打包流水线,高效且智能。
代码实战:“四位一体”解锁批量神技
让我们深入代码,看看这场“集团军行动”是如何策划和执行的。
1. 前端“侦察兵”的装备与战术 - template.php
a. 文件路径定位: template.php (JS部分大约在 1160行 之后)
b. 全面讲解引用:
document.querySelectorAll('.item-checkbox'): 选取页面上所有文件项的多选框。addEventListener('change', ...): 给每个多选框添加一个事件监听器,当它的选中状态发生改变时,就触发一个函数。Array.from(...): 将一个类数组对象(比如querySelectorAll返回的NodeList)转换成一个真正的数组,这样我们就可以使用map等方便的数组方法。checkbox.getAttribute('data-path'): 获取我们预先在HTML中通过data-属性存储的文件路径。这是一种非常常见的在HTML元素上传递数据的方法。
c. 逐行注释与结构化分析:
// 文件路径: template.php
// -------------------
// 核心逻辑:更新批量操作按钮的状态
// -------------------
function updateSelectAllButton() {
// 获取所有文件多选框
const itemCheckboxes = document.querySelectorAll('.item-checkbox');
// 获取被选中的多选框的数量
const checkedCount = document.querySelectorAll('.item-checkbox:checked').length;
// 获取所有批量操作按钮
const deleteSelectedBtn = document.getElementById('deleteSelectedBtn');
const moveSelectedBtn = document.getElementById('moveSelectedBtn');
const downloadSelectedBtn = document.getElementById('downloadSelectedBtn');
// ... 其他按钮
// 如果选中的数量 > 0,则激活按钮;否则禁用按钮
if (checkedCount > 0) {
// 移除 'opacity-50' 和 'cursor-not-allowed' 等禁用样式
// 并将 disabled 属性设为 false
deleteSelectedBtn.disabled = false;
deleteSelectedBtn.classList.remove('opacity-50', 'cursor-not-allowed');
moveSelectedBtn.disabled = false;
moveSelectedBtn.classList.remove('opacity-50', 'cursor-not-allowed');
downloadSelectedBtn.disabled = false;
downloadSelectedBtn.classList.remove('opacity-50', 'cursor-not-allowed');
} else {
// 添加禁用样式,并将 disabled 属性设为 true
deleteSelectedBtn.disabled = true;
deleteSelectedBtn.classList.add('opacity-50', 'cursor-not-allowed');
// ... 对其他按钮做同样操作
}
}
// -------------------
// 核心逻辑:为批量删除按钮绑定事件
// -------------------
document.getElementById('deleteSelectedBtn').addEventListener('click', function() {
const selectedCheckboxes = document.querySelectorAll('.item-checkbox:checked');
if (selectedCheckboxes.length === 0) {
alert('请先选择要删除的文件');
return;
}
// 弹出一个确认框(上一章讲过,这里是动态生成表单的模态框)
// ...
// 这个模态框会生成一个包含所有选中文件路径的隐藏 input 的表单
// <input type="hidden" name="selected_files[]" value="path/to/file1.jpg">
// <input type="hidden" name="selected_files[]" value="path/to/file2.png">
// ...
// 然后提交这个表单
});
// -------------------
// 核心逻辑:为批量下载按钮绑定事件
// -------------------
document.getElementById('downloadSelectedBtn').addEventListener('click', function() {
const selectedCheckboxes = document.querySelectorAll('.item-checkbox:checked');
if (selectedCheckboxes.length === 0) {
alert('请先选择要下载的文件');
return;
}
// 将所有选中的文件路径收集到一个数组中
let selectedFiles = [];
selectedCheckboxes.forEach(checkbox => {
selectedFiles.push(checkbox.getAttribute('data-path'));
});
// 构建一个带参数的 URL,并让浏览器跳转到这个URL
// PHP后端会根据这些参数来识别这是一个批量下载请求
let url = 'index.php?download_selected_files=1&files=' + encodeURIComponent(selectedFiles.join(','));
window.location.href = url;
});
d. 上下文关联:
前端JavaScript是整个批量操作的“发起者”。它负责三件事:1. 监听用户的选择行为,动态更新按钮状态,提供良好的交互反馈。2. 在用户点击操作按钮时,收集所有被选中的文件路径。3. 将这份“路径清单”以合适的方式(POST表单或GET参数)发送给后端index.php。
2. 后端“指挥部”的集团军战术 - index.php
a. 文件路径定位: index.php (批量删除约 142行,批量下载约 505行)
b. 全面讲解引用:
$_POST['selected_files']: 当JS通过POST提交批量操作表单时,后端通过这个变量接收到的就是一个包含所有文件路径的数组。注意name属性selected_files[]中的中括号,它告诉PHP这是一个数组。foreach: 循环遍历数组的最佳“武器”。basename($path): 返回路径中的文件名部分。ZipArchive: PHP内置的用于创建和读取ZIP文件的类。header(): 再次出场!这次它将扮演“快递信息官”的角色,通过设置Content-Type: application/zip和Content-Disposition: attachment等,告诉浏览器这不是普通网页,而是一个需要下载的附件。readfile($path)/fread($handle, ...): 用于读取文件内容并直接输出到浏览器。
c. 逐行注释与结构化分析 (以批量下载为例):
// 文件路径: index.php
// 检查URL中是否有“批量下载”的指令
if (isset($_GET['download_selected_files'])) {
// 从URL的'files'参数中获取文件列表,它们是用逗号分隔的
$selectedFiles = explode(',', $_GET['files']);
// 实例化“打包大师”
$zip = new ZipArchive();
$zipFileName = 'download_' . time() . '.zip'; // 创建一个临时的、带时间戳的zip文件名
// 步骤1:尝试创建并打开zip文件
if ($zip->open($zipFileName, ZipArchive::CREATE) === TRUE) {
// 步骤2:循环遍历文件列表,将每个文件添加到zip包中
foreach ($selectedFiles as $file) {
$filePath = UPLOAD_DIR . '/' . $file;
// 确保文件存在且不是一个目录
if (file_exists($filePath) && is_file($filePath)) {
// 添加文件到zip,第二个参数是文件在zip包内的名称
$zip->addFile($filePath, basename($filePath));
}
}
// 步骤3:完成添加,封上zip包
$zip->close();
// 步骤4:检查zip文件是否成功创建
if (file_exists($zipFileName)) {
// 步骤5:设置HTTP头,告诉浏览器开始下载
header('Content-Description: File Transfer');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $zipFileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($zipFileName));
// 清空输出缓冲区,防止其他内容干扰文件下载
ob_clean();
flush();
// 步骤6:读取zip文件内容并输出给浏览器
readfile($zipFileName);
// 步骤7:下载完成后,删除服务器上的临时zip文件
unlink($zipFileName);
exit; // 终止脚本,下载任务完成
}
}
// ... (处理创建zip失败的错误)
}
d. 上下文关联:
后端的这两块批量处理逻辑(删除和下载)是整个系统的“效率核心”。它们接收来自前端的“文件路径数组”,然后通过一个简单的 foreach 循环,将我们上一章学的单文件操作(unlink)能力放大N倍,或者调用 ZipArchive 这样的高级工具,完成单个文件无法实现的功能。这完美地体现了“Don’t Repeat Yourself (DRY)”原则,用循环优雅地解决了重复性问题。
总结与预告
今天,我们为云盘按上了“涡轮增压”,从“手动挡”一跃升级为“自动挡高配”!通过前后端的精妙配合,我们实现了文件多选、批量删除、批量移动和一键打包下载。这些功能极大地提升了用户体验和操作效率,让我们的云盘项目在“好用”的道路上迈出了一大步。
至此,我们云盘的核心后端功能已经基本开发完毕。但是,一个内外兼修的“高手”,不仅“武功”要高强,“颜值”和“内功心法”也得跟上。
在下一篇文章中,我们将把重心转移到前端,进行一次彻底的“精装修”——“让界面活起来”:前端模板与交互魔法。我们将深入template.php,揭秘Tailwind CSS是如何快速构建漂亮UI的,并剖析项目中的各种JavaScript交互细节,比如模态框、AJAX上传进度条等,让我们的云盘变得更加赏心悦目和“跟手”!
准备好成为一名“全栈艺术家”了吗?我们下期再见!

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



