手把手教你写项目之个人云盘系统全栈开发---第5篇:“一键打包带走!”——批量操作与下载功能的实现

阅读前请先下载项目源码,边读边看源码以加深理解和实操,
源码地址已放于文章末尾!
在这里插入图片描述
在这里插入图片描述

第5篇:“一键打包带走!”——批量操作与下载功能的实现

各位“效率大师”,欢迎回来!

经过前面的修炼,我们的云盘已经掌握了“增删改查”四大神通,成为了一个合格的“文件管家”。但老实说,如果让你整理上百个文件,一个一个去点删除,是不是想死的心都有了?这种重复性的“体力活”,简直是对我们程序员优雅之道的侮辱!

真正的效率,从来都不是单点操作,而是批量处理。今天,我们就来给云盘装上“效率引擎”,实现多文件选择、批量删除、批量移动,以及最酷炫的——一键打包成ZIP下载

核心探秘:从“单兵作战”到“集团军行动”

实现批量操作的思路,本质上是从“一对一”到“一对多”的转变。

  1. 前端(侦察兵):首先,我们需要在前端界面上,给用户一个能够同时“标记”多个目标的工具——多选框(Checkbox)。侦察兵(JavaScript)需要实时监控战场,知道哪些文件被选中了,并将这份“目标清单”上报给指挥部。
  2. 后端(指挥部):指挥部(index.php)不再是接收一个文件名,而是接收一个文件名数组。然后,指挥官(PHP代码)会发布一道循环指令,让士兵(unlink, rename 等函数)对清单上的每一个目标执行相同的操作。

而对于“打包下载”这个特殊任务,指挥部还会请来一位“打包大师”——PHP内置的 ZipArchive 类。

ZipArchive 大师的工作流程:

  1. 领任务:接收到需要打包的文件列表和指定的压缩包名称。
  2. 建空包$zip->open('archive.zip', ZipArchive::CREATE),先创建一个空的ZIP压缩包。
  3. 装东西$zip->addFile($filePath, $fileNameInZip),循环遍历文件列表,把一个个真实的文件“装”进ZIP包里,并可以给它们在压缩包内起个新名字。
  4. 封包$zip->close(),把包装好,封上口。
  5. 发快递:将打包好的ZIP文件通过HTTP响应发送给用户浏览器,并附上特殊的 header 头信息,告诉浏览器:“嘿,这不是一个网页,这是一个需要下载的文件!”
  6. 打扫战场:用户下载完成后,把服务器上临时生成的这个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/zipContent-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上传进度条等,让我们的云盘变得更加赏心悦目和“跟手”!

准备好成为一名“全栈艺术家”了吗?我们下期再见!

源码下载地址:
https://thmail.lanzouu.com/if4Yn34b31cf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THMAIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值