---
title: Attachment
slug: v4-4/0iYR_8veJlbh5lHFPSTbh
docTags: 
createdAt: 2023-10-05T15:27:36.558Z
---

With the `attachment` CRDT you can associate very large amounts of binary data, such as an image, video, and so on, with a document and replicate across peers without conflict. &#x20;

If you have very large amounts of binary data, such as a high-resolution image, video, or some other file above 50 megapixels, or if you have a deeply-embedded  or a large document object, use an `attachment` instead of a regular document.&#x20;

Unlike documents, `attachments` store data outside of the Ditto store running locally in the end-user environment and must be explicitly fetched to replicate across distributed peers.

For more information about Ditto documents, see the *Platform Manual&#x20;*> [CRDT Documents](docId\:utI2PcPnojh74wHSuRD9R).

:::hint{type="warning"}
**Syncing large documents can significantly impact sync performance:**

Caution is advised when handling very large binary data, a deeply embedded document, or a very large document.

Carefully consider using `ATTACHMENT` instead of storing the data directly within a document object.
:::

# Ensuring Uninterrupted Fetching

Maintain a strong reference to `attachmentFetcher` for the entirety of the asynchronous fetch operation by following these guidelines:

- Preserve the `attachmentFetcher` as a globally accessible instance
- Prevent the fetch operation from silently aborting

# Creating and Fetching Attachments

The following snippet demonstrates a use case for leveraging the `attachment` CRDT, as well as the step-by-step process for creating and fetching the `attachment`:

:::hint{type="info"}
If developing in Swift, for a tutorial on how to work with an attachment in a chat app, see [Attachments: Chat App](docId:1OvBhf0k7m0hxaK-HnDbI).
:::

The following snippet demonstrates the process for creating, associating, and fetching an`attachment`.

1. Define a `collection` named `'foo'`.
2. Using Base64-encoded image data and metadata, create an `attachment` object .
3. Upsert a document with an `attachment` in the `collection`.
4. Later, retrieve the document by `_id` and fetch the `attachment` using an `attachmentFetcher`.

:::CodeblockTabs
```kotlin
val testContext = InstrumentationRegistry.getInstrumentation().context
val attachmentStream = testContext.assets.open("attachment_test.png")

val bitmapStream = testContext.assets.open("attachment_test.png")
val attachmentBitmap = BitmapFactory.decodeStream(bitmapStream)
bitmapStream.close()

val metadata = mapOf("name" to "my_image.png")
val attachment = coll.newAttachment(attachmentStream, metadata)

val docId = coll.upsert(mapOf("some" to "string", "my_attachment" to attachment))
val doc = coll.findById(docId).exec()
val attachmentToken = doc!!["my_attachment"].attachmentToken

val fetcher = coll.fetchAttachment(attachmentToken!!) {
    when (it) {
        is Completed -> {
            val attBitmap: Bitmap = BitmapFactory.decodeStream(it.attachment.getInputStream())
        }
        is Progress -> {}
    }
}
```

```javascript
// Define a collection named 'foo' in the Ditto store
const collection = ditto.store.collection('foo')

// Define an image in Base64 format
const myImageBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEW10NBjBBbqAAAAH0lEQVRoge3BAQ0AAADCoPdPbQ43oAAAAAAAAAAAvg0hAAABmmDh1QAAAABJRU5ErkJggg=='

// Convert the Base64 data to a Uint8Array
const myImageBytes = Uint8Array.from(myImageBase64, (character) => character.charCodeAt(0))

// Define metadata
const metadata = { name: 'image.png' }

// Create an attachment object using the image data and metadata
// On Node, you can also pass a file path (string) instead of image data
// and the attachment will be created from that file.
const attachment = await collection.newAttachment(myImageBytes, metadata)

const docID = await collection.upsert({ some: 'string', my_attachment: attachment })

// Later, find the document by ID and then fetch the attachment
const doc = await collection.findByID(docID)
const attachmentToken = doc.at('my_attachment').attachmentToken

// Fetch the attachment
const attachmentFetcher = collection.fetchAttachment(attachmentToken, async (attachmentFetchEvent) => {
  switch (attachmentFetchEvent.type) {
    case 'Completed':
      const fetchedAttachment = attachmentFetchEvent.attachment
      // Do something with attachment
      break

    default:
      console.log('Unable to fetch attachment')
      break
  }
})

// There is also a more convenient way of fetching the attachment
// (AttachmentFetcher implements the `PromiseLike` protocol):
const fetchedAttachment = await collection.fetchAttachment(attachmentToken)
// Do something with attachment
```

```java
String attachmentPath = tempFile.getPath();
Map<String, String> metadata = new HashMap<>();
metadata.put("name", "my_image.png");
DittoAttachment attachment = coll.newAttachment(attachmentPath, metadata);
tempFile.delete();

Map<String, Object> content = new HashMap<>();
content.put("some", "string");
content.put("my_attachment", attachment);
DittoDocumentId docID = coll.upsert(content);

DittoDocument doc = coll.findById(docID).exec();
DittoAttachmentToken attachmentToken = doc.get("my_attachment").getAttachmentToken();

DittoAttachmentFetcher fetcher = coll.fetchAttachment(attachmentToken, event -> {
    if (event.getType() == DittoAttachmentFetchEventType.Completed) {
        DittoAttachment att = event.asCompleted().getAttachment();
        BufferedImage attachmentImage;
        try {
            attachmentImage = ImageIO.read(att.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    } else if (event.getType() == DittoAttachmentFetchEventType.Progress) {
        // do nothing - wait for `Completed` or `Deleted`
    } else {
    }
});
```

```csharp
string attachmentImagePath = Path.Combine(Directory.GetCurrentDirectory(), "attachment_test.png");

var originalBytes = File.ReadAllBytes(attachmentImagePath);

var metadata = new Dictionary<string, string> { { "name", "my_image.png" } };
var attachment = coll.NewAttachment(attachmentImagePath, metadata);

var docId = coll.Upsert(new Dictionary<string, object> { { "some", "string" }, { "my_attachment", attachment } });

var doc = coll.FindById(docId).Exec();
var attachmentToken = doc["my_attachment"].AttachmentToken;
using var fetcher = coll.FetchAttachment(attachmentToken, ev =>
{
    switch (ev)
    {
        case DittoAttachmentFetchEvent.Completed e:
            // Do something with attachment
            break;
        default:
            Console.WriteLine("Unable to fetch attachment");
            break;
    }
});
```

```cpp
string attachmentImagePath = Path.Combine(Directory.GetCurrentDirectory(), "attachment_test.png");

var originalBytes = File.ReadAllBytes(attachmentImagePath);

var metadata = new Dictionary<string, string> { { "name", "my_image.png" } };
var attachment = coll.NewAttachment(attachmentImagePath, metadata);

var docId = coll.Upsert(new Dictionary<string, object> { { "some", "string" }, { "my_attachment", attachment } });

var doc = coll.FindById(docId).Exec();
var attachmentToken = doc["my_attachment"].AttachmentToken;
using var fetcher = coll.FetchAttachment(attachmentToken, ev =>
{
    switch (ev)
    {
        case DittoAttachmentFetchEvent.Completed e:
            // Do something with attachment
            break;
        default:
            Console.WriteLine("Unable to fetch attachment");
            break;
    }
});
```

```rust
// To Implement
```
:::

