---
title: "Attachments"
description: "List, download, add and delete the files attached to a document."
source: "https://www.embedpdf.com/docs/engine/documents/attachments"
---

# Attachments

A PDF can carry other files inside it: a spreadsheet with the numbers behind a report, the
original of a scanned contract. PDF apps list them in an attachments panel.

```ts
const { attachments } = await doc.attachments.list();

for (const attachment of attachments) {
  console.log(attachment.name, attachment.size);
}

const bytes = await doc.attachments.download(attachments[0].ref);
```

## List

`list()` returns the attached files, sorted by name. It doesn't read the files themselves, so it's
quick however large they are.

| Field         | What it is                                                         |
| :------------ | :----------------------------------------------------------------- |
| `ref`         | The attachment's address. Pass it to `download()` and `delete()`.  |
| `name`        | The file name, such as `'figures.xlsx'`.                           |
| `mimeType`    | The file type, such as `'application/pdf'`, or `null`.             |
| `description` | A description, or `null`.                                          |
| `size`        | The size in bytes, or `null`.                                      |
| `checksum`    | The file's MD5 checksum, as lowercase hex, or `null`.              |
| `createdAt`   | When the file was created, as an ISO date, or `null`.              |
| `modifiedAt`  | When the file was last changed, as an ISO date, or `null`.         |
| `index`       | Its position in the list. Changes when files are added or deleted. |

A field reads `null` when the PDF doesn't say.

## Download one

`download(ref)` returns the file's bytes. Its name and type are the ones the list gave you, so
handing it to the user takes both:

```ts
const bytes = await doc.attachments.download(attachment.ref);

const url = URL.createObjectURL(new Blob([bytes], { type: attachment.mimeType ?? '' }));
const link = document.createElement('a');
link.href = url;
link.download = attachment.name;
link.click();
URL.revokeObjectURL(url);
```

A [file attachment annotation's](https://www.embedpdf.com/docs/engine/annotations/file-attachments) file downloads the
same way: `page.annotations.downloadResource(ref, 'file')` returns its bytes.

## Attach a file

`create()` takes the bytes and a name, and returns the new attachment:

```ts
const { attachment } = await doc.attachments.create({
  data: bytes,
  name: 'figures.xlsx',
  mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  description: 'Numbers for the Q2 report',
});
```

`data` is a `Uint8Array`, an `ArrayBuffer` or a `Blob`. A `File` the user picked brings its own
name and type, so it's enough on its own:

```ts
const file = input.files![0];
await doc.attachments.create({ data: file });
```

Only `data` and a name are needed; a `File` brings its name, and a `File` or `Blob` brings its
type. The engine never guesses a type: without one, `mimeType` reads `null`. It records the size,
the checksum and the time you attached it.

Names are unique in a document: attaching a second file with a name that's already there fails
with `InvalidArg`. Rename it and try again.

## Delete

```ts
await doc.attachments.delete(attachment.ref);
```

The attachment disappears from the list. Its bytes stay in earlier versions of the file until you
save with `mode: 'rewrite'`; see [Good to know](#deleted-files-and-saving).

## Undo

`create()` and `delete()` are changes: each returns its `opId` in `meta`, and
`doc.apply({ undoOf: opId })` undoes it. An undo of a delete brings the same file back, with its
bytes, description and dates, and fires `attachments.restored`. To add or remove several files as
one step, apply their ops as one change. See [Changes & undo](https://www.embedpdf.com/docs/engine/documents/changes).

```ts
const { meta } = await doc.attachments.delete(attachment.ref);
await doc.apply({ undoOf: meta.opId }); // the file is back
```

An undo leaves alone what someone changed since: the undo of an attach doesn't delete a file
someone else attached under that name since, even with the same bytes, and the undo of a delete
doesn't bring a file back while another file has its name.

## Attachments or file-attachment annotations

A PDF can hold a file in two places:

- **An attachment** (this page) belongs to the whole document. PDF apps list it in their
  attachments panel.
- **A [file attachment annotation](https://www.embedpdf.com/docs/engine/annotations/file-attachments)** is pinned to a spot
  on a page, shown as an icon, and can carry a comment.

They're separate: `doc.attachments.list()` doesn't include files pinned to pages, and deleting an
attachment never touches an annotation.

## Permissions

| Call                   | Needs                                        |
| :--------------------- | :------------------------------------------- |
| `list()`               | `doc.open`                                   |
| `download()`           | `doc.download`: the file leaves the document |
| `create()`, `delete()` | `doc.attachments.modify`                     |

So a user can see which files a document carries without being able to take them out.

## Good to know

### Deleted files and saving

Deleting an attachment removes it from the document as it is now. An incremental save still
contains the version you opened, and with it the deleted file. When a file must really be gone,
save with `mode: 'rewrite'`. See [Saving](https://www.embedpdf.com/docs/engine/documents/saving#incremental-or-rewrite).

### Names and refs

An attachment's ref is its name in the PDF's list of files, which is why names are unique. When
you stored a name yourself, `toAttachmentRef('figures.xlsx')` turns it back into a ref. A file
another app attached may show a different `name` than the one in its ref; always pass the ref.

Adding or deleting a file changes the `index` of the files after it in name order, never their
refs.

### Large files

The browser holds a file it downloads in memory, so the local engine refuses a file that decodes
to more than `ATTACHMENT_LIMITS.decodedBytes` (256 MB) with `PayloadTooLarge`. That goes for
attachments and file attachment annotations alike; `details.limit` names the limit. A file's own size can't be trusted for this: a small compressed
file can decode to gigabytes.

### Events

Other code can follow changes with the `attachments.created`, `attachments.deleted` and
`attachments.restored` events. See [Events](https://www.embedpdf.com/docs/engine/concepts/events).
