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

105 天前
 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();
    }
}
2006 次点击
所在节点    程序员
12 条回复
kele1997
105 天前
https://go.dev/play/p/RPK_Xmq0RPK

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

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

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

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

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

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

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

© 2021 V2EX