请问如何实现大文件夹即时浏览器下载

7 天前
 saveai

我用的是 php ,问了一圈 AI ,告诉我使用 ZipStream ,但是我试了同样需要等待压缩的时候非常久,而不是像群晖 NAS 那样,立即开始下载,且浏览器不显示最终文件大小。 请问 v 友这是如何做到的

public function downloadFolderNew()
{
    $path = $this->request->param("path", "");
    if (!empty($path)) {
        // 将路径转换为项目的公共路径
        $path = str_replace("D:\\phpproject\\test\\public\\", public_path(), $path);
    }

    if (!is_dir($path)) {
        $this->error("路径不存在");
    }

    $fileName = $this->request->param("file_name", "") ?: uniqid();
    $zipFileName = $fileName . ".zip";

    // 设置响应头
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . $zipFileName . '"');
    header('X-Accel-Buffering: no'); // 关闭 Nginx 的输出缓冲

    // 禁用输出缓冲区
    if (ob_get_level()) {
        ob_end_clean();
    }
    // 立即输出部分内容,浏览器开始下载
    echo "\xEF\xBB\xBF"; // 避免 PHP 输出缓冲,立即开始下载
    flush();
    // 创建一个 ZIP 流
    $zip = new ZipStream\ZipStream(null, [
        'outputStream' => 'php://output' // 将输出直接定向到浏览器
    ]);
    // 递归添加文件夹内容到 ZIP
    $this->addFolderToZip($zip, $path, '');

    // 结束 ZIP 流
    $zip->finish();
    exit();
}

private function addFolderToZip($zip, $folder, $zipPath)
{
    // 创建迭代器,遍历文件夹中的文件
    $files = new \RecursiveIteratorIterator(
        new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS),
        \RecursiveIteratorIterator::LEAVES_ONLY
    );
    foreach ($files as $file) {
        $filePath = $file->getRealPath();
        $relativePath = substr($filePath, strlen($folder) + 1); // 获取文件相对路径
        if (!$file->isDir()) {
            // 确保文件路径和内容正确添加到 ZIP 中
            $zip->addFileFromPath($zipPath . $relativePath, $filePath);
        }
        // 刷新输出缓冲,逐步发送数据到浏览器
        flush();
    }
}
1693 次点击
所在节点    程序员
12 条回复
kele1997
7 天前
https://go.dev/play/p/RPK_Xmq0RPK

我用了 go 语言,是能够支持边压缩,边下载的
dode
7 天前
流式处理,把 zip 的输出流设置为浏览器的输入流
ntedshen
7 天前
不看注释都知道是 nginx 用户。。。
因为我写 php 的时候也没搞定那个疑似是缓存的缓存问题。。。。。。。。。。。。。。。。

群晖没测过。。。
mega 点击下载 zip 文件以后是前端分片下载单个文件然后前端自己打包的。。。
我现在自己写 nas 也是造 mega 造的。。。
okakuyang
7 天前
因为 zip 的文件格式特性,应该是可以是实现一边压缩一边传输的。由于压缩还在进行中,程序还没有计算出最后的文件大小,所以不会开始就告诉浏览器文件大小。
zhuangzhuang1988
7 天前
java 的话有个 netty
中间插入个中间层就可以了
https://netty.io/4.1/xref/io/netty/handler/codec/http/HttpContentEncoder.html#L194
wxf666
7 天前
@okakuyang #4 能不能遍历下文件,提前算好总大小,以《打包存储》形式压缩,传输过程中由 gzip 压缩呢?
billccn
7 天前
你把 CompressionMethod 设置成 STORE (就是不压缩)试试?或者把 defaultDeflateLevel 调整一下。

另外显示的大小是在 HTTP 头里的,你不提前算好所有文件+zip 开销,等 HTTP 头都发完了才开始遍历目录那肯定不会有大小的。
leonshaw
7 天前
有没有办法续传呢
sunchuo
7 天前
okakuyang
7 天前
@wxf666 额,不是很了解 gzip 的机制,但是估计和 zip 差不多是把单个文件进行压缩,zip 设置成打包不压缩,理论上会很快生成 zip 文件,然后传输给 gzip ,感觉不是很靠谱。
siweipancc
6 天前
206 请求+队列+二进制合并
siweipancc
6 天前
需要有自定义下载条,建议楼主参考下 asmr.one 的实现。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1072046

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX