javascript - 如何在 javascript 中合并和解压缩(.zip、.z01、.z02)(仅限客户端)?

标签 javascript reactjs axios

我正在尝试分部分(.zip、.z01、.z02)等下载来自 api 调用的拆分 zip 文件。
在 Javascript 中,我想即时下载这些文件并创建一个组合的 .zip 文件,然后保存解压缩的文件。 (我不需要创建组合 zip)我只需要 test.pdf 文件,该文件已被拆分为多个 zip。我想下载那个pdf文件。
例如
test.pdf (18 mb) 分为 (.zip 10mb) 和 (.z01 8mb) 两个 zip 文件。目前我正在将它们下载为 blob,然后将它们合并。生成的 zip 已损坏。下面是我试过的代码。

Promise.all([
  axios.get(`https://testapi.com/folder/file.zip`, {
    responseType: "blob",
    headers: {
      Accept: "application/json",
    },
  }),
  axios.get(`https://testapi.com/folder/file.z01`, {
    responseType: "blob",
    headers: {
      Accept: "application/json",
    },
  }),
]).then((obj) => {
  let blob: any = [];
  obj.forEach((e: any) => {
    blob.push(e.data);
  });
  const newBlob = new Blob(blob, { type: "octet/stream" });
  saveAs(newBlob, "zip.zip"); // but it's currupted
});

最佳答案

您可以使用 JSZipFileSaver为了实现这一点。请注意,input 中的文件按字母顺序排列。所以这个片段需要你的文件相应地命名。我使用了 input为了使测试更容易,您可以轻松地将其调整为每个文件的 axios 请求。
该代码段实际上在此网页上不起作用,因为代码段 iframe 的政策不允许下载,因此您必须通过其他方式尝试。

const objectWithoutProperties = (obj, keys) => {
  const target = {};
  for (let i in obj) {
    if (keys.indexOf(i) >= 0) continue;
    if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
    target[i] = obj[i];
  }
  return target;
}

const mergeFiles = (a, b) => {
  if (!a) return b;
  const intersect = Object.keys(a.files).filter((x) =>
    Object.keys(b.files).includes(x)
  );

  a.files = { ...a.files, ...objectWithoutProperties(b.files, intersect) };

  return Promise.all(
    intersect.map((file) =>
      Promise.all([
        a.file(file).async("arraybuffer"),
        b.file(file).async("arraybuffer"),
      ])
        .then((x) => new Blob(x))
        .then((x) => a.file(file, x))
    )
  ).then(() => a);
};

const MultipleZipMerger = () => {
  function change(e) {
    Promise.all([...e.target.files].map(JSZip.loadAsync))
      .then((x) =>
        x.reduce(
          (acc, cur) => acc.then((x) => mergeFiles(x, cur)),
          Promise.resolve()
        )
      )
      .then((x) => x.generateAsync({ type: "blob" }))
      .then((x) => saveAs(x, "hello.zip"));
  }
  
  return <input onChange={change} type="file" multiple />;
};

ReactDOM.render(<MultipleZipMerger />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.1.0/jszip-utils.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>

<div id="root"></div>

这是此代码的独立版本,您可以将其作为本地文件进行测试:
<!doctype html>

<html>
</head>

<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.6.0/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.1.0/jszip-utils.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>

<div id="root"></div>
<script>
  
const objectWithoutProperties = (obj, keys) => {
  const target = {};
  for (let i in obj) {
    if (keys.indexOf(i) >= 0) continue;
    if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
    target[i] = obj[i];
  }
  return target;
}

const mergeFiles = (a, b) => {
  if (!a) return b;
  const intersect = Object.keys(a.files).filter((x) =>
    Object.keys(b.files).includes(x)
  );

  a.files = { ...a.files, ...objectWithoutProperties(b.files, intersect) };

  return Promise.all(
    intersect.map((file) =>
      Promise.all([
        a.file(file).async("arraybuffer"),
        b.file(file).async("arraybuffer"),
      ])
        .then((x) => new Blob(x))
        .then((x) => a.file(file, x))
    )
  ).then(() => a);
};

const MultipleZipMerger = () => {
  function change(e) {
    Promise.all([...e.target.files].map(JSZip.loadAsync))
      .then((x) =>
        x.reduce(
          (acc, cur) => acc.then((x) => mergeFiles(x, cur)),
          Promise.resolve()
        )
      )
      .then((x) => x.generateAsync({ type: "blob" }))
      .then((x) => saveAs(x, "hello.zip"));
  }

  return React.createElement("input", {
    onChange: change,
    type: "file",
    multiple: true,
  });
};

ReactDOM.render(
  React.createElement(MultipleZipMerger, null),
  document.getElementById("root")
);

  
</script>
</body>
</html>

关于javascript - 如何在 javascript 中合并和解压缩(.zip、.z01、.z02)(仅限客户端)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67846534/

相关文章:

javascript - Axios:在单独的文件中使用 baseUrl 会导致在 axios 请求 header 中设置身份验证 token 时出现问题

javascript - 如何使用ajax从数据库中检索特定数据

javascript - 在 Safari 中加载源映射和调试?

javascript - Javascript for 循环中的多个计数器

javascript - 如何从 Redux 存储获取状态

ajax - 使用 axios 捕获错误

javascript - 使用 Axios 将数据从 Vue.js 传递到 PHP

reactjs - store.getState() 在 index.js 中返回空值

javascript - 组件无法监听 react-router

javascript - 如何在 axios 的 .then() 链中访问之前的 promise 响应?