Create and extract tar archives
A tar archive bundles many files and directories into a single file, usually compressed with gzip to produce the familiar .tar.gz format. The Standard Library provides TarStream and UntarStream in jsr:@std/tar, both built on web standard streams so they compose with CompressionStream and file streams without buffering whole archives in memory.
import { TarStream, type TarStreamInput, UntarStream } from "jsr:@std/tar";
import { normalize } from "jsr:@std/path";We prepare a directory with two files to archive.
const dir = await Deno.makeTempDir();
await Deno.mkdir(`${dir}/project`);
await Deno.writeTextFile(`${dir}/project/readme.txt`, "A sample project.");
await Deno.writeTextFile(`${dir}/project/config.json`, `{"name":"sample"}`);To create an archive, pipe a stream of entries through a TarStream. A directory entry only needs a path. A file entry also needs its size and a readable stream of its contents. Piping the result through a CompressionStream produces a .tar.gz file.
const readmeStat = await Deno.stat(`${dir}/project/readme.txt`);
const configStat = await Deno.stat(`${dir}/project/config.json`);
const archive = await Deno.create(`${dir}/project.tar.gz`);
const inputs: TarStreamInput[] = [
{ type: "directory", path: "project" },
{
type: "file",
path: "project/readme.txt",
size: readmeStat.size,
readable: (await Deno.open(`${dir}/project/readme.txt`)).readable,
},
{
type: "file",
path: "project/config.json",
size: configStat.size,
readable: (await Deno.open(`${dir}/project/config.json`)).readable,
},
];
await ReadableStream.from(inputs)
.pipeThrough(new TarStream())
.pipeThrough(new CompressionStream("gzip"))
.pipeTo(archive.writable);
console.log((await Deno.stat(`${dir}/project.tar.gz`)).size); // 258To extract, run the pipeline in reverse: decompress the bytes, then pipe them through an UntarStream and iterate over the entries.
const outDir = `${dir}/extracted`;
await Deno.mkdir(outDir);
const tarball = await Deno.open(`${dir}/project.tar.gz`);
const entries = tarball.readable
.pipeThrough(new DecompressionStream("gzip"))
.pipeThrough(new UntarStream());Directory entries have no readable stream. For file entries, piping the readable into a file writes it to disk. Paths inside an archive are not trustworthy, so normalize them before touching the file system.
for await (const entry of entries) {
console.log(entry.path);
const target = `${outDir}/${normalize(entry.path)}`;
if (entry.readable === undefined) {
await Deno.mkdir(target, { recursive: true });
continue;
}
const file = await Deno.create(target);
await entry.readable.pipeTo(file.writable);
}
// project
// project/readme.txt
// project/config.jsonThe extracted files match the originals.
console.log(await Deno.readTextFile(`${outDir}/project/readme.txt`)); // A sample project.
console.log(await Deno.readTextFile(`${outDir}/project/config.json`)); // {"name":"sample"}Reading and writing files requires the -R and -W permissions.
await Deno.remove(dir, { recursive: true });Run this example locally using the Deno CLI:
deno run -R -W https://docs.deno.com/examples/scripts/tar_archives.ts