Как JavaScript распаковывает ZIP-файлы онлайн?

внешний интерфейс JavaScript
Как JavaScript распаковывает ZIP-файлы онлайн?

Я считаю, что все знакомы с ZIP-файлами.Если вы хотите открыть локальный ZIP-файл, вам необходимо установить программное обеспечение для распаковки, которое поддерживает распаковку ZIP-файлов. Но что делать, если предварительно распакованный ZIP-файл лежит на сервере? Самое простое решение — загрузить файл локально, а затем использовать программное обеспечение для распаковки, поддерживающее формат ZIP, для его распаковки. Так можно ли распаковать ZIP-файлы онлайн? Ответ положительный. Далее Brother Abao представит два решения для распаковки ZIP-файлов в режиме онлайн: распаковка в браузере и распаковка на сервере.

Перед введением двух схем для декомпрессирующих Zip-файлов онлайн Давайте кратко посмотрите на формат ZIP-файла.

1. Введение в формат ZIP

Формат файла ZIP — это формат файлов для сжатия данных и хранения документов, ранее известный как Deflate, изобретенный Филом Кацем, объявившим об этом формате в январе 1989 года. ZIP обычно использует суффикс «.zip», а его формат MIME — «application/zip». В настоящее время формат ZIP является одним из нескольких основных форматов сжатия, наряду с конкурентами, включая формат RAR и формат 7z с открытым исходным кодом.

ZIP — это довольно простой формат архива, который сжимает каждый файл отдельно, позволяя извлекать отдельные файлы без чтения дополнительных данных. Теоретически этот формат позволяет использовать разные алгоритмы для разных файлов. На практике, однако, большинство ZIP-архивов используют алгоритм Каца DEFLATE.

После краткого ознакомления с форматом ZIP брат Абао сначала представитJSZipСхема распаковки браузера для этой библиотеки.

Следуйте «Дорога полного стека», чтобы прочитать 4 бесплатные электронные книги (всего более 30 000 загрузок) и 11 руководств по Vue 3 для продвинутых пользователей.

2. Схема декомпрессии браузера

JSZipэто инструмент для создания, чтения и редактирования.zipБиблиотека JavaScript файла, библиотека поддерживает большинство браузеров, и конкретная совместимость показана на следующем рисунке:

На самом деле естьJSZipС помощью этой библиотеки несложно реализовать функцию распаковки ZIP-файлов онлайн на стороне браузера. Поскольку чиновник предоставил намРазархивируйте локальные файлы, разархивируйте удаленные файлы и создайте ZIP-файлы.полный пример. Ладно, без лишних слов, давайте шаг за шагом реализуем функцию распаковки ZIP-файлов онлайн.

2.1 Определите класс инструмента

Функция онлайн-распаковки ZIP-файла на стороне браузера может быть разделена наСкачивайте ZIP-файлы, анализируйте ZIP-файлы и отображайте ZIP-файлы3 небольшие функции. Принимая во внимание возможность повторного использования функций, Brother Abao инкапсулирует логику загрузки ZIP-файлов и разбора ZIP-файлов вExeJSZipВ классе:

class ExeJSZip {
  // 用于获取url地址对应的文件内容
  getBinaryContent(url, progressFn = () => {}) {
    return new Promise((resolve, reject) => {
      if (typeof url !== "string" || !/https?:/.test(url))
        reject(new Error("url 参数不合法"));
      JSZipUtils.getBinaryContent(url, { // JSZipUtils来自于jszip-utils这个库
        progress: progressFn,
        callback: (err, data) => {
          if (err) {
            reject(err);
          } else {
            resolve(data);
          }
        },
      });
    });
  }
  
  // 遍历Zip文件
  async iterateZipFile(data, iterationFn) {
    if (typeof iterationFn !== "function") {
      throw new Error("iterationFn 不是函数类型");
    }
    let zip;
    try {
      zip = await JSZip.loadAsync(data); // JSZip来自于jszip这个库
      zip.forEach(iterationFn);
      return zip;
    } catch (error) {
      throw new error();
    }
  }
}

2.2 Распаковать ZIP-файл онлайн

использоватьExeJSZipПример класса, мы можем легко реализовать функцию распаковки Zip-файлов онлайн:

HTML-код
<p>
  <label>请输入ZIP文件的线上地址:</label>
  <input type="text" id="zipUrl" />
</p>
<button id="unzipBtn" onclick="unzipOnline()">在线解压</button>
<p id="status"></p>
<ul id="fileList"></ul>
JS-код
const zipUrlEle = document.querySelector("#zipUrl");
const statusEle = document.querySelector("#status");
const fileList = document.querySelector("#fileList");
const exeJSZip = new ExeJSZip();

// 执行在线解压操作
async function unzipOnline() {
  fileList.innerHTML = "";
  statusEle.innerText = "开始下载文件...";
  const data = await exeJSZip.getBinaryContent(
    zipUrlEle.value,
    handleProgress
  );
  let items = "";
  await exeJSZip.iterateZipFile(data, (relativePath, zipEntry) => {
    items += `<li class=${zipEntry.dir ? "caret" : "indent"}>
      ${zipEntry.name}</li>`;
  });
  statusEle.innerText = "ZIP文件解压成功";
  fileList.innerHTML = items;
}

// 处理下载进度
function handleProgress(progressData) {
  const { percent, loaded, total } = progressData;
  if (loaded === total) {
    statusEle.innerText = "文件已下载,努力解压中";
  }
}

Ну как пройти на стороне браузераJSZipЭта библиотека для реализации функции распаковки ZIP-файлов онлайн была введена, давайте посмотрим на результаты выполнения приведенного выше примера:

Теперь, когда мы можем распаковать ZIP-файл онлайн, некоторые друзья могут спросить, можем ли мы предварительно просмотреть распакованный файл? Ответ положительный, потому что библиотека JSZip предоставляет намfileAPI, через этот API мы можем прочитать содержимое указанного файла. Например, используйте вот такzip.file("amount.txt").async("arraybuffer"), а затем мы можем выполнить соответствующую операцию, чтобы реализовать функцию предварительного просмотра файла.

Следует отметить, что решение на основе JSZip не идеально, оно имеет некоторые ограничения. Например, он не поддерживает распаковку зашифрованных ZIP-файлов.При распаковке больших файлов браузеры под IE 10 могут столкнуться со сбоем. Кроме того, у него есть некоторые другие ограничения, которые Брат Абао не будет здесь подробно объяснять. Заинтересованные друзья, вы можете прочитатьLimitations of JSZipсоответствующий контент в статье.

Поскольку схема декомпрессии браузера имеет некоторые недостатки, особенно в случае онлайн-декомпрессии больших файлов, для решения этой проблемы мы можем рассмотреть возможность использования схемы декомпрессии сервера.

3. Схема распаковки сервера

Решение для декомпрессии сервера состоит в том, чтобы позволить пользователям распаковывать онлайн через идентификатор файла или имя файла.koaа такжеnode-stream-zipЭти две библиотеки показывают, как реализовать функцию распаковки ZIP-файлов онлайн на сервере. если ты правkoaЕсли вы еще этого не знаете, я предлагаю вам сначала прочитать официальную документацию koa.

const path = require("path");
const Koa = require("koa");
const cors = require("@koa/cors");
const Router = require("@koa/router");
const StreamZip = require("node-stream-zip");

const app = new Koa();
const router = new Router();
const ZIP_HOME = path.join(__dirname, "zip"); // ZIP文件的根目录
const UnzipCaches = new Map(); // 保存已解压的文件信息

router.get("/", async (ctx) => {
  ctx.body = "服务端在线解压ZIP文件示例(阿宝哥)";
});

// 注册中间件
app.use(cors());
app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
  console.log("app starting at port 3000");
});

В приведенном выше коде мы использовали@koa/corsа также@koa/routerДва промежуточного программного обеспечения и создали простое приложение Koa. Основываясь на приведенном выше коде, давайте зарегистрируем маршрут, который обрабатывает онлайн-распаковку указанного имени файла.

3.1 Разархивируйте указанный ZIP-файл в соответствии с именем файла

app.js
router.get("/unzip/:name", async (ctx) => {
  const fileName = ctx.params.name;
  let filteredEntries;
  try {
    if (UnzipCaches.has(fileName)) { // 优先从缓存中获取
      filteredEntries = UnzipCaches.get(fileName);
    } else {
      const zip = new StreamZip.async({ file: path.join(ZIP_HOME, fileName) });
      const entries = await zip.entries();
      filteredEntries = Object.values(entries).map((entry) => {
        return {
          name: entry.name,
          size: entry.size,
          dir: entry.isDirectory,
        };
      });
      await zip.close();
      UnzipCaches.set(fileName, filteredEntries);
    }
    ctx.body = {
      status: "success",
      entries: filteredEntries,
    };
  } catch (error) {
    ctx.body = {
      status: "error",
      msg: `在线解压${fileName}文件失败`,
    };
  }
});

В приведенном выше коде мы передаемZIP_HOMEа такжеfileNameчтобы получить окончательный путь к файлу, затем используйтеStreamZipобъект для выполнения операций распаковки. Во избежание повторных операций декомпрессии Брат Абао определяетUnzipCachesОбъект кэша, используемый для сохранения информации о распакованном файле. После определения вышеуказанных маршрутов давайте проверим соответствующие функции.

3.2 Онлайн-распаковка ZIP-файлов

HTML-код
<p>
  <label>请输入ZIP文件名:</label>
  <input type="text" id="fileName" value="kl_161828427993677" />
</p>
<button id="unzipBtn" onclick="unzipOnline()">在线解压</button>
<p id="status"></p>
<ul id="fileList"></ul>
JS-код
const fileList = document.querySelector("#fileList");
const fileNameEle = document.querySelector("#fileName");

const request = axios.create({
  baseURL: "http://localhost:3000/",
  timeout: 10000,
});

async function unzipOnline() {
  const fileName = fileNameEle.value;
  if(!fileName) return;
  const response = await request.get(`unzip/${fileName}`);
  if (response.data && response.data.status === "success") {
    const entries = response.data.entries;
    let items = "";
    entries.forEach((zipEntry) => {
      items += `<li class=${zipEntry.dir ? "caret" : "indent"}>${
        zipEntry.name
      }</li>`;
    });
    fileList.innerHTML = items;
  }
}

Результат после успешного запуска приведенного выше примера показан на следующем рисунке:

Теперь, когда мы реализовали распаковку указанного ZIP-файла в соответствии с именем файла, можем ли мы предварительно просмотреть файл по указанному пути в сжатом файле? Ответ да, используяzipпредоставленный объектentryData(entry: string | ZipEntry): Promise<Buffer>метод для чтения содержимого файла по указанному пути.

3.3 Предварительный просмотр файлов по указанному пути в ZIP-файле

app.js
router.get("/unzip/:name/entry", async (ctx) => {
  const fileName = ctx.params.name; // ZIP压缩文件名
  const entryPath = ctx.query.path; // 文件的路径
  try {
    const zip = new StreamZip.async({ file: path.join(ZIP_HOME, fileName) });
    const entryData = await zip.entryData(entryPath);
    await zip.close();
    ctx.body = {
      status: "success",
      entryData: entryData,
    };
  } catch (error) {
    ctx.body = {
      status: "error",
      msg: `读取${fileName}中${entryPath}文件失败`,
    };
  }
});

В приведенном выше коде мы передаемzip.entryDataметод для чтения содержимого файла по указанному пути, он возвращаетBufferобъект. Когда внешний интерфейс получает данные, ему также необходимоBufferобъект, преобразованный вArrayBufferобъект, соответствующий метод обработки выглядит следующим образом:

function toArrayBuffer(buf) {
  let ab = new ArrayBuffer(buf.length);
  let view = new Uint8Array(ab);
  for (let i = 0; i < buf.length; ++i) {
    view[i] = buf[i];
  }
  return ab;
}

определенныйtoArrayBufferПосле функции мы можем вызватьapp.jsОпределенный API для реализации функции предварительного просмотра, конкретный код выглядит следующим образом:

async function previewZipFile(path) {
  const fileName = fileNameEle.value; // 获取文件名
  const response = await request.get(
    `unzip/${fileName}/entry?path=${path}`
  );
  if (response.data && response.data.status === "success") {
    const { entryData } = response.data;
    const entryBuffer = toArrayBuffer(entryData.data);
    const blob = new Blob([entryBuffer]);
    // 使用URL.createObjectURL或blob.text()读取文件信息
  }
}

Так как полный образец кода содержит много контента, Brother Abao не будет помещать конкретный код. Заинтересованные партнеры могут посетить следующий адрес, чтобы просмотреть образец кода.

gist.GitHub.com/Semelinker/3…

Примечание. Вышеприведенный код предназначен только для справки, пожалуйста, отрегулируйте его в соответствии с фактическим бизнесом.

4. Резюме

В этой статье Brother Abao представляет две схемы онлайн-распаковки ZIP-файлов.В реальных проектах рекомендуется использовать схему распаковки на сервере. Это может не только решить проблему совместимости браузера, но и решить проблему онлайн-распаковки больших файлов, а также облегчить последующее расширение для поддержки других форматов сжатия.

Следуйте «Дорога полного стека», чтобы прочитать 4 бесплатные электронные книги (всего более 30 000 загрузок) и 11 руководств по Vue 3 для продвинутых пользователей.Друзья, которые хотят вместе изучать TS/Vue 3.0, могут добавить Abaoge WeChat —— semlinker.

5. Справочные ресурсы