Skip to content

在线文件下载并压缩

js
const files = [{ url: '', filename: '' }];

fetchAndZipFiles({
  files, // 文件
  zipName: ``,
  onProgress: (progress) => {
    // 下载进度
  },
  onError: (error) => {
    // 下载失败
  },
  onSuccess: () => {
    // 下载成功
  },
});
js
import { zipSync } from 'fflate';
import { saveAs } from 'file-saver';

/**
 * 压缩文件并下载
 * @param opts
 * @param {string} opts.zipName - 压缩包名称
 * @param {Array<{ url: string; filename: string }>} opts.files - 文件列表
 * @param {() => void} [opts.onSuccess] - 下载成功的回调
 * @param {(v: number) => void} [opts.onProgress] - 下载进度的回调
 * @param {(error: Error) => void} [opts.onError] - 下载出错的回调
 */
export const fetchAndZipFiles = async (opts: {
  zipName: string,
  files: Array<{ url: string, filename: string }>,
  onSuccess?: () => void,
  onProgress?: (v: number) => void,
  onError?: (error: Error) => void,
}) => {
  const { zipName, files, onProgress, onError, onSuccess } = opts;
  onProgress?.(5);

  const zipFiles: Record<string, Uint8Array> = {};
  const totalFiles = files.length;

  let downloadedFiles = 0;

  try {
    for (const file of files) {
      if (!file.filename) {
        throw new Error('File name is missing');
      }

      const response = await fetch(file.url);

      if (!response.ok) {
        throw new Error(`Failed to fetch ${file.url}: ${response.statusText}`);
      }

      const blob = await response.blob();
      const arrayBuffer = await blob.arrayBuffer();
      zipFiles[file.filename] = new Uint8Array(arrayBuffer);

      downloadedFiles += 1;
      // -- 更新进度以反映基于文件数量的总进度的90%
      onProgress?.(5 + Math.round((downloadedFiles / totalFiles) * 90));
    }

    setTimeout(() => {
      const zipData = zipSync(zipFiles);
      const zipBlob = new Blob([zipData], { type: 'application/zip' });

      saveAs(zipBlob, `${zipName}.zip`);

      // -- 保存文件后,将进度设置为100%
      onProgress?.(100);
      onSuccess?.();
    }, 500);
  } catch (error) {
    onError?.(error);
  }
};