# Packer (Multi-format Archiver) Links: [Home](../index.md) An **SGApps.IO product**: a unified archive creation and extraction module with **zero external dependencies**, written in-house. Every container format is implemented from scratch using only native browser APIs — including a complete LZMA encoder and decoder for 7z. Proprietary, source-available — see `modules/archiver/LICENSE`. ## Supported Formats | Format | Create | Extract | Implementation | |---|---|---|---| | **ZIP** | yes | yes | Own container code; DEFLATE via the browser's `CompressionStream` | | **TAR** | yes | yes | Own code, POSIX ustar | | **GZIP** | yes | yes | Own container code; compression via the browser's `CompressionStream` | | **TAR.GZ** | yes | yes | TAR + GZIP combined | | **7Z** | yes | yes | Own code, own LZMA encoder/decoder (reader: single-folder archives with plain headers, LZMA1 or Copy codec -- 7-Zip's default LZMA2 + compressed-header output is not yet supported) | | **RAR** | -- | listing + stored entries | Own code, v4 + v5 header parser (compressed entries are listed, not decoded) | ## Loading the Module ```js Application.require("archiver/packer").then(function (Packer) { // Packer is ready }); ``` ## API ### `Packer(format)` Create a new empty archive. Returns an `ApplicationPrototype` instance directly (synchronous). - `format` *(string)* -- `'zip'`, `'tar'`, `'gzip'`, `'tar.gz'` (or `'tgz'`), `'7z'` ```js var pack = Packer('zip'); pack.addFile('hello.txt', 'Hello'); pack.format(); // 'zip' ``` ### `Packer(format, source)` Open an existing archive. **Always returns a `Promise`** that resolves with the `ApplicationPrototype` instance, regardless of format. - `format` *(string)* -- `'zip'`, `'tar'`, `'gzip'`, `'tar.gz'`, `'7z'`, `'rar'` - `source` *(Uint8Array | ArrayBuffer | string)* -- existing archive data ```js Packer('tar.gz', data).then(function (pack) { pack.format(); // 'tar.gz' pack.files(); // [{name, size, isDirectory}, ...] }); ``` > **Important:** When opening an archive (with `source`), the return value is always a Promise -- even for synchronous formats like TAR and 7Z. Always use `.then()` to access the instance. ### `Packer.open(data [, format])` Auto-detect format and open an archive. Always returns a `Promise`. ```js Packer.open(uint8ArrayData).then(function (pack) { console.log('Format:', pack.format()); }); ``` ### `Packer.detectFormat(data)` Detect archive format from magic bytes. Returns `'zip'`, `'tar'`, `'gzip'`, `'7z'`, `'rar'`, or `null`. ### `Packer.FORMATS` Constants object: `{ ZIP: 'zip', TAR: 'tar', GZIP: 'gzip', TAR_GZ: 'tar.gz', SEVEN_ZIP: '7z', RAR: 'rar' }` ## Instance Methods The API is **identical** across all formats. The same methods work whether you created a ZIP, TAR, TAR.GZ, 7Z, or any other format: | Method | Returns | Description | |---|---|---| | `addFile(path, data [, options])` | `app` | Add a file. Options: `{ mtime, mode }` | | `addFolder(path)` | `app` | Add a directory entry | | `remove(path)` | `app` | Remove an entry by path | | `files()` | `Array` | List all entries `[{name, size, isDirectory, compressed}]` | | `getFile(path [, asText])` | `Promise` | Get file content. `asText=true` returns string | | `forEach(callback)` | -- | Iterate entries: `callback(name, entry)` | | `format()` | `string` | Get the archive format | | `generate(type)` | `Promise` | Generate archive. `type`: `'uint8array'`, `'blob'`, `'arraybuffer'` | ## Use Cases ### Create a ZIP and download it ```js Application.require("archiver/packer").then(function (Packer) { var pack = Packer('zip'); pack.addFile('readme.txt', 'This is a test archive'); pack.addFile('data/config.json', JSON.stringify({ version: 1 })); pack.addFolder('images/'); pack.generate('blob').then(function (blob) { var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'archive.zip'; a.click(); URL.revokeObjectURL(url); }); }); ``` ### Create a TAR.GZ bundle ```js Application.require("archiver/packer").then(function (Packer) { var pack = Packer('tar.gz'); pack.addFile('index.html', '

Hello

'); pack.addFile('style.css', 'body { margin: 0; }'); pack.addFile('app.js', 'console.log("ready");'); pack.generate('blob').then(function (blob) { console.log('TAR.GZ size:', blob.size, 'bytes'); }); }); ``` ### Open and inspect an uploaded archive ```js Application.require("archiver/packer").then(function (Packer) { document.getElementById('fileInput').addEventListener('change', function (e) { var file = e.target.files[0]; var reader = new FileReader(); reader.onload = function () { Packer.open(new Uint8Array(reader.result)).then(function (pack) { console.log('Format:', pack.format()); pack.files().forEach(function (f) { console.log(f.name, f.size, 'bytes', f.isDirectory ? '(dir)' : ''); }); // Extract a specific file as text pack.getFile('readme.txt', true).then(function (text) { console.log('Content:', text); }); }); }; reader.readAsArrayBuffer(file); }); }); ``` ### Round-trip: create TAR.GZ then re-open it ```js Application.require("archiver/packer").then(function (Packer) { // Create var pack = Packer('tar.gz'); pack.addFile('hello.txt', 'Hello World'); pack.addFile('data.json', '{"key":"value"}'); pack.generate('uint8array').then(function (archive) { console.log('Created:', archive.length, 'bytes'); // Re-open (always use .then -- returns a Promise) return Packer('tar.gz', archive); }).then(function (pack2) { console.log('Format:', pack2.format()); // 'tar.gz' console.log('Files:', pack2.files()); // [{name:'hello.txt',...}, ...] return pack2.getFile('hello.txt', true); }).then(function (text) { console.log('Content:', text); // 'Hello World' }); }); ``` ### Create a compressed 7z archive ```js Application.require("archiver/packer").then(function (Packer) { var pack = Packer('7z'); pack.addFile('large-data.csv', generateCSVData()); pack.generate('uint8array').then(function (data) { console.log('7z size:', data.length, 'bytes (LZMA compressed)'); }); }); ``` ### Format auto-detection from fetch ```js Application.require("archiver/packer").then(function (Packer) { fetch('/api/download/backup').then(function (res) { return res.arrayBuffer(); }).then(function (buf) { var format = Packer.detectFormat(new Uint8Array(buf)); console.log('Detected:', format); // 'zip', '7z', 'tar', etc. return Packer.open(new Uint8Array(buf)); }).then(function (pack) { console.log('Files:', pack.files()); }); }); ``` ## Notes - **Return types:** `Packer(format)` returns the app directly. `Packer(format, source)` and `Packer.open(data)` always return a Promise -- use `.then()` to get the app instance. - ZIP uses DEFLATE compression via `CompressionStream` API. Falls back to Store method in browsers without `CompressionStream` support. - 7Z uses LZMA compression (full native encoder/decoder). Falls back to Copy codec if LZMA produces larger output. - RAR extraction is read-only. RAR creation is not supported because the format is proprietary. - GZIP format compresses only the first entry. Use TAR.GZ for multiple files. - The instance API (`addFile`, `files`, `getFile`, `generate`, etc.) is identical across all formats.