---
title: ATTACHMENT
slug: v4-5/data-types/attachment
docTags: 
createdAt: 2023-10-05T15:27:36.558Z
---

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

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

# Merge Strategy: Last-Write-Wins

The `REGISTER` and the `ATTACHMENT` type follow the *last-write-wins* principle to determine the value that ultimately takes precedence and merges across distributed peers. With this approach, each peer seeing the change will sequence it in the same order as every other peer, resulting in the same single value.&#x20;

For example, one flight attendant updates a customer's seat number to `6` and another to seat `9`. When the two conflicting versions merge, the edit with the highest timestamp wins.

Put another way, by enforcing the last-write-wins merge strategy, for events A and B, where B is a result of A, event A always occurs before B.

Described as *unsurprising* and *meaningful*, the last-write-wins strategy means that, rather than arbitrarily resolving all conflicting changes to some predefined value, the value that results and propagates across peers as the single source of truth both reflects the actual end-user input and the update made by the last writer.&#x20;

# 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\:CdkWGf9mQl4fTfHe8nJOs).
:::

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. `INSERT` 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 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 = '123'
const newDocument = { _id: docId, my_attachment: attachment }

await ditto.store.execute(`
  INSERT INTO COLLECTION your_collection_name (my_attachment ATTACHMENT)
  DOCUMENTS (:newDocument)`,
  { newDocument })

// Later, find the document by ID and then fetch the attachment
const result = await ditto.store.execute(`
  SELECT *
  FROM your_collection_name
  WHERE _id = :docId`,
  { docId })

const attachmentToken = result[0].items('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);

string docId = "123";
var insertArgs = new Dictionary<string, object>();
insertArgs.Add(
  "newDocument",
  new { _id = docId, myAttachment = attachment });

await ditto.Store.ExecuteAsync(
  "INSERT INTO your_collection_name DOCUMENTS (:newDocument)",
  insertArgs);

var result = await ditto.Store.ExecuteAsync(
  "SELECT * FROM your_collection_name WHERE _id = :docId",
  new Dictionary<string, string> {{"docId", docId}}); 

var attachmentToken = result.Items[0].Value["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
```
:::

