Skip to content <?xml version="1.0" encoding="UTF-8"?>

Downloading and Decompressing a Zip File With Deno

Deno is a new runtime for Javascript and Typescript. Like Node, it uses V8 under the hood however unlike Node, it is written in Rust and it does not require a package manager. Deno intends to correct the design flaws of Node and offer a more secure platform. Because the runtime is a single binary and the first class Typescript support does not require a build step, Deno seems particularly well-suited for serverless applications and functions as a service.

However, one feature I am particularly interested in is Deno’s ability to execute remote code. It is possible to tell Deno to execute a remote program simply by passing a URL to the deno run command. To demonstrate this functionality, I wrote a small script to download a zip file and unzip it on the local filesystem.

You can find the source code of the following example on github.

Downloading a binary file

Deno follows the browser Javascript API so it is really straightforward to get started; the use of promises combined with async/await makes the code wonderfully readable.

To download the zip file we can simply use the browser fetch API. The Deno runtime provides the necessary API to work with the filesystem.

/**
 * Download the source file and write it into the destination
 */
async function download(source: string, destination: string): Promise<void> {
  // We use browser fetch API
  const response = await fetch(source);
  const blob = await response.blob();

  // We convert the blob into a typed array
  // so we can use it to write the data into the file
  const buf = await blob.arrayBuffer();
  const data = new Uint8Array(buf);

  // We then create a new file and write into it
  const file = await Deno.create(destination);
  await Deno.writeAll(file, data);

  // We can finally close the file
  Deno.close(file.rid);
}

Decompressing a zip file

We have the possibility to execute arbitrary commands in a subprocess so we can unzip the file by calling directly the unzip command.

/**
 * Unzip the file
 */
async function unzip(filepath: string): Promise<void> {
  // We execute the command
  // The function returns details about the spawned process
  const process = Deno.run({
    cmd: ["unzip", filepath],
    stdout: "piped",
    stderr: "piped",
  });

  // We can access the status of the process
  const { success, code } = await process.status();

  if (!success) {
    // We retrieve the error
    const raw = await process.stderrOutput();
    const str = new TextDecoder().decode(raw);
    throw new Error(`$Command failed: code ${code}, message: ${str}`);
  } else {
    // Similarly to access the command output
    const raw = await process.output();
    const str = new TextDecoder().decode(raw);
    console.log(str);
  }
}

Putting things together

top-level await is not yet supported so we still need to wrap our code in an IIFE.

(async function () {
  const filename = "archive.zip";
  const url =
    "https://raw.githubusercontent.com/mickaelvieira/deno-download-unzip-file/master/archive.zip";

  try {
    await download(url, filename);
    await unzip(filename);

    // Move into the newly unarchived directory
    Deno.chdir("./archive");

    // Do something with the archive's content

  } catch (e) {
    console.error(e);
    Deno.exit(1);
  }
}());

Executing the script

Deno executes the code in a secure sandbox. By design, a script is not allowed to execute a command, access the filesystem or even the network so we need to explicitly allow those operations.

deno run \
  --allow-net \
  --allow-read \
  --allow-write \
  --allow-run https://raw.githubusercontent.com/mickaelvieira/deno-download-unzip-file/master/installer.ts