Working with Files

Working with files can be tricky in Node.js and the browser. Here are the general do’s and don’ts, as well as examples on how to handle files.

Memory Usage

When working with files, always be mindful of memory usage. Reading a large file into memory can cause your application to crash. If you need to read a large file, consider using streams.

In the browser

All modern browsers and Deno implement the File API. It is the recommended way to interact with the Fileforge API and SDKs in the browser.

The File object associates a content stream with important metadata like the file’s name, size, and MIME type.

Creating a File instance

Creating a File object from a Blob, Buffer or String

1const pdfFile = new File([pdfData], "example.pdf", { type: "application/pdf" });
2const htmlFile = new File([`<p>Hello World!</p>`], "example.html", {
3 type: "text/html",
4});

Creating a File object from a File input

1const fileInput = document.getElementById("fileInput") as HTMLInputElement;
2const file = fileInput.files[0];

Consuming a File

Creating a URL for a File

Creating an object URL for a file is useful when you need to display the file in a specific context like an <iframe>, or if you want to trigger a download.

1const url = URL.createObjectURL(pdfFile);
2
3// Use the URL in an iframe
4const iframe = document.createElement("iframe");
5iframe.src = url;
6
7// Open the file in a new tab
8window.open(url);

Converting a ReadableStream to a File

This example is only applicable in browser-like environments. In Node.js environments, the SDK response is a Readable rather than a ReadableStream.

When interacting with API methods that return a file, the SDK will return a ReadableStream. Here is how to convert it back to a File object.

1// Note: this API call returns a ReadableStream only in browser-like environments
2const file = (await ff.pdf.generate(html, {
3 options: {
4 host: false,
5 },
6})) as ReadableStream;
7
8// Note: this accumulates the entire file in memory
9const blob = await new Response(file).blob();
10const fileObject = new File([blob], "file.pdf", {
11 type: "application/pdf",
12});
13
14const url = URL.createObjectURL(fileObject);
15
16window.open(url);

In Node.js

Node.js does not have a built-in File object like the browser. We recommend working with the polyfill from formdata-node that provides a similar API to the browser. formdata-node is shipped with the Fileforge SDK.

Reading a File

From an existing file

1import { File } from "formdata-node";
2import { readFile } from "fs/promises";
3
4const pdfFile = new File([await readFile("example.pdf")], "example.pdf", {
5 type: "application/pdf",
6});

From an existing file, streamed

1import { File } from "formdata-node";
2import { createReadStream } from "fs";
3
4// Create a File object from a stream
5class BlobFromStream {
6 #stream;
7
8 constructor(stream, size) {
9 this.#stream = stream;
10 this.size = size;
11 }
12
13 stream() {
14 return this.#stream;
15 }
16
17 get [Symbol.toStringTag]() {
18 return "Blob";
19 }
20}
21
22const pdfFile = new File(
23 [new BlobFromStream(createReadStream("example.pdf"))],
24 "example.pdf",
25 {
26 type: "application/pdf",
27 }
28);

Without using File

In some cases, you may not want or need to use the File object. You can work with the file directly using streams. Note that some endpoints, such as generate and merge rely on file names to determine root files and links. Not passing Files may cause issues with these endpoints.

1const file = (await ff.pdf.generate(createReadStream("./index.html"), {
2 options: {
3 host: false,
4 },
5})) as Readable;

Consuming a SDK response

When interacting with API methods that return a file, the SDK will return a Readable. Here is how to consume it.

1import { createWriteStream } from "fs";
2
3const file = (await ff.pdf.generate(createReadStream("./index.html"), {
4 options: {
5 host: false,
6 },
7})) as Readable;
8
9file.pipe(createWriteStream("file.pdf"));