Files
Load files — whether static or generated dynamically by a data loader — using the built-in FileAttachment
function. This is available by default in Markdown, but you can import it explicitly like so:
import {FileAttachment} from "observablehq:stdlib";
The FileAttachment
function takes a path and returns a file handle. This handle exposes:
name
- the file’s name (such asvolcano.json
),mimeType
- MIME type (such asapplication/json
),lastModified
- modification time (in milliseconds since epoch), andsize
- size in bytes .
FileAttachment("volcano.json")
Like a local import, the path is relative to the calling code’s source file: either the page’s Markdown file or the imported local JavaScript module. To load a remote file, use fetch
, or use a data loader to download the file at build time.
Calling FileAttachment
doesn’t actually load the file; the contents are only loaded when you invoke a file contents method. For example, to load a JSON file:
const volcano = FileAttachment("volcano.json").json();
The value of volcano
above is a promise. In other code blocks, the promise is implicitly awaited and hence you can refer to the resolved value directly.
volcano
Static analysis
The FileAttachment
function can only be passed a static string literal; constructing a dynamic path such as FileAttachment(`frame${i}.png`)
is invalid syntax. Static analysis is used to invoke data loaders at build time, and ensures that only referenced files are included in the generated output during build. This also allows a content hash in the file name for cache breaking during deploy.
If you have multiple files, you can enumerate them explicitly like so:
const frames = [
FileAttachment("frame1.png"),
FileAttachment("frame2.png"),
FileAttachment("frame3.png"),
FileAttachment("frame4.png"),
FileAttachment("frame5.png"),
FileAttachment("frame6.png"),
FileAttachment("frame7.png"),
FileAttachment("frame8.png"),
FileAttachment("frame9.png")
];
None of the files in frames
above are loaded until a content method is invoked, for example by saying frames[0].image()
.
For missing files, file.size
and file.lastModified
are undefined. The file.mimeType
is determined by checking the file extension against the mime-db
media type database; it defaults to application/octet-stream
.
Supported formats
FileAttachment
supports a variety of methods for loading file contents:
method | return type |
---|---|
file.arquero |
Arquero Table |
file.arrayBuffer |
ArrayBuffer |
file.arrow |
Arrow Table |
file.blob |
Blob |
file.csv |
Array |
file.dsv |
Array |
file.html |
Document |
file.image |
HTMLImageElement |
file.json |
Array , Object , etc. |
file.parquet |
Arrow Table |
file.sqlite |
SQLiteDatabaseClient |
file.stream |
ReadableStream |
file.text |
string |
file.tsv |
Array |
file.xlsx |
Workbook |
file.xml |
Document |
file.zip |
ZipArchive |
The contents of a file often dictate the appropriate method — for example, an Excel XLSX file is almost always read with file.xlsx
. When multiple methods are valid, choose based on your needs. For example, you can load a CSV file using file.arquero
to load it into Arquero, or even using file.text
to implement parsing yourself.
In addition to the above, you can get the resolved absolute URL of the file using file.href
:
FileAttachment("volcano.json").href
See file-based routing for additional details.
Basic formats
The following common basic formats are supported natively.
Text
To load a humble text file, use file.text
:
const hello = FileAttachment("hello.txt").text();
hello
By default, file.text
expects the file to be encoded in UTF-8. To use a different encoding, pass the desired encoding name to file.text
.
const pryvit = FileAttachment("pryvit.txt").text("utf-16be");
pryvit
JSON
To load a JSON (JavaScript Object Notation) file, use file.json
FileAttachment("volcano.json").json()
A common gotcha with JSON is that it has no built-in date type; dates are therefore typically represented as ISO 8601 strings, or as a number of milliseconds or seconds since UNIX epoch.
Media
To display an image, you can use a static image in Markdown such as <img src="horse.jpg">
or ![horse](horse.jpg)
. Likewise, you can use a video
or audio
element. Per file-based routing, static references to these files are automatically detected and therefore these files will be included in the built output.
<video src="horse.mp4" autoplay muted loop controls></video>
If you want to manipulate an image in JavaScript, use file.image
. For example, below we load an image and invert the RGB channel values.
const canvas = document.querySelector("#horse-canvas");
const context = canvas.getContext("2d");
const horse = await FileAttachment("horse.jpg").image();
context.drawImage(horse, 0, 0, canvas.width, canvas.height);
const data = context.getImageData(0, 0, canvas.width, canvas.height);
for (let j = 0, k = 0; j < canvas.height; ++j) {
for (let i = 0; i < canvas.width; ++i, k += 4) {
data.data[k + 0] = 255 - data.data[k + 0];
data.data[k + 1] = 255 - data.data[k + 1];
data.data[k + 2] = 255 - data.data[k + 2];
}
}
context.putImageData(data, 0, 0);
(The images above are from Eadweard Muybridge’s studies of animal locomotion.)
Markup
The file.xml
method reads an XML file and returns a promise to a Document
; it takes a single argument with the file’s MIME-type, which defaults to "application/xml"
. The file.html
method similarly reads an HTML file; it is equivalent to file.xml("text/html")
.
Binary formats
Load binary data using file.blob
to get a Blob
, or file.arrayBuffer
to get an ArrayBuffer
. For example, to read Exif image metadata with ExifReader:
import ExifReader from "npm:exifreader";
const buffer = await FileAttachment("horse.jpg").arrayBuffer();
const tags = ExifReader.load(buffer);
display(tags);
To read a file incrementally, get a ReadableStream
with file.stream
. For example, to count the number of bytes in a file:
const stream = await FileAttachment("horse.jpg").stream();
const reader = stream.getReader();
let total = 0;
while (true) {
const {done, value} = await reader.read();
if (done) break;
total += value.length;
}
display(total);
Routing
Attached files live in the source root (typically src
) alongside your Markdown pages. For example, say index.md
has some JavaScript code that references FileAttachment("quakes.csv")
:
.
├─ src
│ ├─ index.md
│ └─ quakes.csv
└─ …
On build, any files referenced by FileAttachment
will automatically be copied to the _file
folder under the output root (dist
), here resulting in:
.
├─ dist
│ ├─ _file
│ │ └─ quakes.e5f2eb94.csv
│ ├─ _observablehq
│ │ └─ … # additional assets
│ └─ index.html
└─ …
FileAttachment
references are automatically rewritten during build; for example, a reference to quakes.csv
might be replaced with _file/quakes.e5f2eb94.csv
. (As with imports, file names are given a content hash, here e5f2eb94
, to improve performance.) Only the files you reference statically are copied to the output root (dist
), so nothing extra or unused is included in the built site.
Imported local modules can use FileAttachment
, too. In this case, the path to the file is relative to the importing module in the same fashion as import
; this is accomplished by resolving relative paths at runtime with import.meta.url
.
Some additional assets are automatically promoted to file attachments and copied to _file
. For example, if you have a <link rel="stylesheet" href="style.css">
declared statically in a Markdown page, the style.css
file will be copied to _file
, too (and the file name given a content hash). The HTML elements eligible for file attachments are audio
, img
, link
, picture
, and video
.