toolgarden.xyz
中文
ZIPfile compressionfile extractionbrowser localfrontend engineering

How to Build Online File Compression and Extraction with Browser-Local ZIP

Online compression and extraction do not always need a server upload. File API, fflate, Blob URLs, and a folder-tree model are enough to create ZIP files, extract archives, browse directories, and download individual files in the browser.

ToolGarden tools prioritize browser-local processing, so files and text do not need to be uploaded to a server.

Published September 8, 20269 min readBy ToolGarden

Online file compression and extraction sound like server-side work: upload files, process them remotely, then download the result. But for ZIP archives, modern browsers can handle most everyday workflows directly. Files can stay on the user device while the page reads local data, runs the archive algorithm, and creates download links.

ToolGarden ZIP Compressor and ZIP Extractor follow that model. The compressor turns multiple File objects into one ZIP Blob. The extractor reads ZIP bytes, parses entries, rebuilds a folder tree from paths, and lets users download individual files. No API route receives the file contents.

The Data Flow

Compression:
File input / drag drop
  -> File API reads Blob data
  -> normalize archive paths
  -> fflate zipSync
  -> Blob(application/zip)
  -> URL.createObjectURL download

Extraction:
ZIP File
  -> arrayBuffer
  -> inspect central directory for filename encoding
  -> fflate unzipSync
  -> filter unsafe paths
  -> build folder tree
  -> create Blob download for each file

The important boundary is that the page can read files the user explicitly chooses, but it does not need to send those bytes to a server. The server delivers JavaScript, CSS, and static assets; the actual work happens inside the browser process.

Why ZIP, Not RAR or 7z?

ZIP has mature JavaScript implementations for both creation and extraction, and the output works almost everywhere. Windows, macOS, Linux, and mobile operating systems can usually open a .zip without extra software. For a web utility, that makes ZIP the reliable default.

RAR and 7z are different. Extraction can be evaluated separately, but archive creation is not as open, lightweight, or browser-friendly as ZIP creation. Rather than shipping fragile format support, the compressor exports ZIP only and the extractor focuses on ZIP first.

Compression: Multiple Files to One ZIP Blob

The compression page should stay thin: selected files, output filename, compression level, and button state. The real implementation belongs in a utility module such as lib/utils/zip.ts, where a function receives filename + blob entries and returns a discriminated result instead of touching React state.

type ZipCompressionOutcome =
  | {
      ok: true;
      blob: Blob;
      filename: string;
      fileCount: number;
      originalSize: number;
      outputSize: number;
      durationMs: number;
    }
  | { ok: false; code: 'empty_selection' | 'empty_file' | 'zip_failed' };

Before compression, normalize archive paths. Folder uploads may expose webkitRelativePath, while normal file selection only has file.name. Replace backslashes, drop empty segments, remove . and .., and rename duplicate paths so later files do not silently overwrite earlier ones.

fflate zipSync handles the archive generation. Compression levels range from 0 to 9: 0 is closer to packaging without compression, while 9 may save more space at the cost of time. A default of 6 is a practical speed-to-size balance.

Extraction: Entries First, Folder Tree Second

The extractor should not flatten every file into one long list. ZIP entries contain paths such as docs/readme.txt and docs/assets/logo.png; splitting those paths gives a natural folder tree. Folder rows can expand and collapse, while file rows show size and a download button.

interface ZipExtractedEntry {
  path: string;
  name: string;
  size: number;
  blob: Blob;
}

interface TreeNode {
  name: string;
  path: string;
  children: Map<string, TreeNode>;
  entry?: ZipExtractedEntry;
}

Keep extraction and display concerns separate. The utility function returns entries; the React component builds TreeNode data and owns interaction state such as expanded folders. That keeps the archive parser testable and the UI easier to change.

Why Chinese Filenames Become Garbled

Many ZIP filenames are UTF-8, but not every archive sets the UTF-8 flag. Older archives from Chinese Windows environments often store filename bytes as GBK or GB18030. If the unzip library interprets those bytes as Latin-1 or another fallback, Chinese names become unreadable.

A practical fix is to inspect raw filename bytes in the ZIP central directory. When an entry does not have the UTF-8 filename flag, decode those bytes with GB18030 and map that name back to the Latin-1 name returned by the unzip library. Standard UTF-8 ZIP files remain untouched, while legacy Chinese filenames display correctly.

Downloads Use Blob URLs

Both compression and extraction can download from Blob URLs. The compressed result is an application/zip Blob. Each extracted file is an application/octet-stream Blob. The page calls URL.createObjectURL, assigns it to an anchor href, and sets the download filename.

Blob URLs are temporary browser resources. A long-running tool should revoke them when clearing results or unmounting the component, especially when users process multiple large archives in one session.

Security and Reliability Boundaries

  • Zip Slip: archive paths may contain ../ segments, so clean paths before showing or downloading them.
  • Zip bombs: a tiny archive may expand into huge output, so warn about large files and add entry-count or total-size limits in stricter deployments.
  • Encrypted ZIP: password flows and compatibility are complex; unsupported encrypted archives should fail clearly.
  • Memory pressure: hundreds of MB or thousands of files can slow down or crash a browser tab.
  • Filename collisions: compression should rename duplicate archive paths instead of overwriting silently.

Where the Code Belongs

In ToolGarden, ZIP tools follow the Harness Engineering pattern: metadata in the registry, copy in messages, archive logic in lib/utils/zip.ts, and page components limited to state, file selection, folder expansion, and download clicks. Home cards, the Other tools menu, breadcrumbs, SEO metadata, sitemap, and llms files all derive from the same metadata.

Frequently asked questions

Q.Does online ZIP compression need a server?

No. A browser can read user-selected files with File API, compress or extract ZIP archives with JavaScript, and download results through Blob URLs. The server only needs to serve the web app.

Q.Why do Chinese filenames look garbled after ZIP extraction?

Some ZIP archives omit the UTF-8 filename flag while storing names as GBK or GB18030 bytes. Inspecting the central directory and applying a GB18030 fallback fixes many legacy Chinese ZIP filenames.

Q.How large can browser-local ZIP processing go?

It depends on browser and device memory. Normal attachment-sized archives work well, but hundreds of MB, thousands of files, or suspicious archives can be slow or fail. Production tools should communicate those limits clearly.