Skip to content

Binary File Handling

If the automation needs to upload or download files — zips, images, documents — page.evaluate() sits between Node.js and the browser, and it can only pass JSON-serializable data. Buffers and file handles don't survive the crossing.

Problem: You Need to Upload a File

The file exists on disk in Node.js, but the upload endpoint expects a request from the browser context (possibly with session cookies).

Solution: base64-encode on the Node side, decode to a Blob in the browser, POST via FormData:

javascript
import { readFileSync } from "fs";

const zipBuffer = readFileSync("data.zip");
const zipBase64 = zipBuffer.toString("base64");

const result = await page.evaluate(
  async ({ base64, fileName }) => {
    const binary = atob(base64);
    const bytes = Uint8Array.from(
      binary,
      (char) => char.charCodeAt(0),
    );
    const blob = new Blob(
      [bytes],
      { type: "application/zip" },
    );
    
    const form = new FormData();
    form.append("file", blob, fileName);
    
    const res = await fetch("/api/upload", {
      method: "POST",
      body: form,
    });
    return {
      status: res.status,
      body: await res.json(),
    };
  },
  { base64: zipBase64, fileName: "data.zip" },
);

Auth headers are included automatically because fetch runs in the page context.

Problem: You Need to Download a File

The file is on the server and may be behind authentication. Direct HTTP requests from Node.js would require extracting session tokens.

Solution: fetch in the browser context, base64-encode the response, return to Node:

javascript
const base64 = await page.evaluate(async (url) => {
  const res = await fetch(url);
  const buffer = await res.arrayBuffer();
  const bytes = new Uint8Array(buffer);
  let binary = "";
  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }
  return btoa(binary);
}, "/api/download/file.zip");

const fileBuffer = Buffer.from(base64, "base64");
writeFileSync("downloaded.zip", fileBuffer);

Why Base64

page.evaluate() serializes arguments and return values as JSON. Binary data cannot survive JSON serialization — bytes get corrupted or lost. Base64 encodes binary as ASCII text, which JSON handles cleanly. The ~33% size overhead is acceptable for the file sizes typical in automation workflows.