浏览器实际大文件下载解决方案

背景

现在大多数业务都是和内容生产相关的业务,我们公司实际业务是频繁和素材打交道,尤其是电商短视频(抖音,快手,tiktok)打交道,文件的上传和下载尤为重要,目前国内好像还没发现靠存储收费的大型服务商,文件的频繁下载这是一个常见的场景,

比如我们一开始遇到一个实际业务问题是,用户上传的交付物都是素材(图片,视频),都是一批一批的,我们想着是打包下载,用户下载后也方便找,更有规则,但是现在正常的存储的服务商基本上不提供这样的服务,

开会的时候讨论方案,方案其实挺多的,问AI或者cursor 也是实现非常容易,但是没有考虑实际业务场景, 如果下载是一个高频操作,且文件非常大,如何处理

方式一:浏览器插件下载(推荐)

javascripts 复制代码
export const batchDownload = (detail: DownloadRequest) => {
  detail?.list?.forEach((item) => {
    let filename = '';
    if (detail?.dir) {
      filename = `${detail.dir}/${item.filename}`;
    } else {
      filename = item.filename;
    }
    chrome.downloads.download({
      url: item.url,
      filename,
      saveAs: detail?.saveAs || false,
      // 防止重名覆盖
      conflictAction: 'uniquify'
    });
  });
};

优点

  • 流水线下载,可以下载的时候在前面加一个目录名字,会将文件下载到目录下面(别想多了,有些人这里就想偏了,不能是任意目录,是浏览器默认设置的下载目录下你可以创建的目录)
  • 下载进度用户可见,不吃内存,随时可以取消

缺点

  • 需要用户安装插件
  • 插件需要支持的多每家的标准都不一样(Chrome, firefox, edge, 360极速浏览器,360安全浏览器)
  • 开发框架也是比较多,难选(我们用的是Wxt)
  • 上架(chrome插件国内需要自己安装,firefox可以自动化走内部托管,一键化发布,自动更新)

提示,记得开发插件的时候记得样式问题多检查一下,最好使用沙箱机制。

方式二:使用跨平台的方案,比如套壳electron

优点

  • 自定义下载,完全突破浏览器限制
  • 自定义程度高

缺点

  • 不容易管理内存,内存如果不能合理分配,会直接内存爆炸(实际项目我试过,用户电脑死机了)
  • 浏览器开发和客户端开发不是一个标准,客户端其实更多是基于内存和业务的合理分配和调度

方式三:FileSystem Access API + Streams API + ZipWriterStream (Web Streams 压缩库)

javascript 复制代码
import { ZipWriterStream } from '@zip.js/zip.js';
export const startZipStreamDownload = async (
  fileHandle: FileSystemFileHandle,
  detail: DownloadRequest
): Promise<DownloadResult> => {
  const result: DownloadResult = {
    success: true,
    successFiles: [],
    failedFiles: []
  };

  let writable: FileSystemWritableFileStream | null = null;
  let zipStream: ZipWriterStream | null = null;
  let pipePromise: Promise<void> | null = null;

  try {
    writable = await fileHandle.createWritable();
    zipStream = new ZipWriterStream({ zip64: true });
    pipePromise = zipStream.readable.pipeTo(writable);

    for (let i = 0; i < detail.list.length; i++) {
      const item = detail.list[i];
      const filename = item.filename;

      try {
        const response = await fetch(item.url);

        if (!response.ok) {
          console.warn(`跳过文件:HTTP ${response.status} - ${item.url}`);
          result.failedFiles.push({
            url: item.url,
            filename,
            result: 'failed',
            error: `HTTP ${response.status}`
          });
          continue;
        }

        if (!response.body) {
          throw new Error('Response body is null');
        }

        await response.body.pipeTo(zipStream.writable(filename));

        result.successFiles.push({
          url: item.url,
          filename,
          result: 'completed',
          error: ''
        });
        console.log(`已完成: ${filename}`);
      } catch (innerError) {
        console.error(`下载中断或出错 [${filename}]:`, innerError);
        result.failedFiles.push({
          url: item.url,
          filename,
          result: 'failed',
          error:
            innerError instanceof Error
              ? innerError.message
              : String(innerError)
        });
        // 继续下一个文件
      }
    }

    // 正常关闭 ZIP 流
    await zipStream.close();
    await pipePromise;
  } catch (error) {
    console.error('ZIP 流写入失败:', error);
    result.success = false;
    // 安全清理
    try {
      if (writable) {
        await writable.abort();
      }
    } catch {
      // 忽略 abort 错误
    }
  }

  // 设置最终状态
  if (result.failedFiles.length > 0 && result.successFiles.length === 0) {
    result.success = false;
  }

  return result;
};

优点

  • 下载10GB,浏览器也可能只消耗几十MB的内存,流水线处理数据,不需一次性将整个大文件加载到内存中,极大地节省了内存开销。
  • 不需要浏览器插件就可以实现

缺点

  • 兼容性差,Chrome/Edge 等 Chromium 系浏览器支持较好,Safari 和 Firefox 的支持程度有限。
  • 需要用户点击授权
  • 页面标签不可关闭(一旦关闭,文件下载失败,服务器还要计算流量费用)
  • 下载进度不可见

方式四:客户端使用blob流将所有的字节流都堆到内存中,全都接收完毕后进行下载

代码就懒得写了,随便搜搜都是这种方式

优点

  • 打包压缩下载可以实现

缺点

  • 文件大了,内存爆炸,浏览器分配的内存是4G,但实际安全内存只有2G,超过2G的内存直接被V8干掉了
  • 浏览器标签关闭也是下载失败,服务器还是会计算出网费用

我们就是使用方式1,方式2也有,如果需要插件的朋友,可以到时候评论区说,有需求我可以发布一下,都是可以通过api调用下载的。或者有人想二次开发的话我也可以直接开源发布一下。看需求吧

如果有想获取抖音实际视频地址 或者 下载保存抖音无水印视频 需求的朋友可以使用下面的工具直接获取视频地址,这个原本是公司内网用的
Easydown

相关推荐
xiangxiongfly91514 天前
Node http
http·node·文件上传·请求·文件下载·响应
Cshaosun23 天前
阿里云宝塔面板部署vue+nodejs项目并实现https访问操作流程
vue.js·阿里云·https·node.js·宝塔·文件下载
yangchanghua1116 个月前
Springboot 文件下载(Excel) + Vue前端下载按钮
前端·vue.js·spring boot·文件下载
亦世凡华、10 个月前
React--》文件下载优化技巧与最佳实践
前端·经验分享·react.js·前端框架·文件下载
不修×蝙蝠1 年前
SpringMVC(五)实现文件上传
java·spring·springmvc·文件管理·文件上传·文件下载
nrsc1 年前
[实用小代码java]-如何将对象存储服务器上的文件下载到客户端
java·服务器·oss·文件下载·octet-stream
太自由1 年前
SpringMVC之 文件上传和下载
spring·springmvc·resource·文件上传下载·文件下载·springmvc文件
bobo-rs1 年前
Go语言操作文件上传和下载应用教程
开发语言·golang·iphone·文件上传·文件下载·go文件
代码星辰1 年前
功能实现——通过阿里云 OSS 实现文件管理
阿里云·文件上传·oss·文件下载