> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Large Binary Files

> This article provides an overview and how-to instructions for working with attachments within Ditto. Attachments in Ditto are represented using the ATTACHMENT data type, which you can use to store binary data, such as images, alongside queryable descriptive information, such as file name and description.

See [Syncing Data](/key-concepts/syncing-data#attachments) for more information on attachments in Ditto.

## ATTACHMENT Object

The ATTACHMENT object stores your attachment outside the local device or object
storage.

* The metadata component is a set of string values providing extra details about the file or complex document, such as name and size, among other identifying information.
* The blob store is an array of bytes representing your large file or complex document encoded as an ATTACHMENT data type. (See [Blob Store](#blob-store))

## Attachment Token

The attachment token  encapsulates information Ditto uses to identify and
retrieve the full attachment when needed; as in, you've explicitly fetched it in
your app.

The following structure and corresponding table provide an overview of the schema the attachment token uses to model the data it contains:

```json JSON theme={null}
{
  "id": "string",
  "len": 42,
  "metadata": {}
}
```

Here are the attachment token properties you can query to increase the efficiency of fetching operations in your app:

| **Field** | **Detail**    | **Description**                                                                                       |
| --------- | ------------- | ----------------------------------------------------------------------------------------------------- |
| id        | Attachment ID | A cryptographic hash of the attachment's binary contents. (See [Organizing by ID](#organizing-by-id)) |
| len       | Blob Length   | The size of the blob data in terms of length (len) in bytes. (See [Blob Store](#blob-store))          |
| metadata  | Metadata      | Additional information about the attachment, such as its name, type, and so on.                       |

## Example Document

Here is an attachment token structured within a document. In this structure, the
picture attachment token acts as a placeholder for the full attachment storing a
JPEG file named `car_stock_photo.jpg`:

```json Document theme={null}
{
    "_id": "abc123",
    "make": "Hyundai",
    "year": 2018,
    "color": "black",
    "picture": {
        "id": "<unique identifier>",
        "metadata": {
            "mime_type": "image/jpeg",
            "name": "car_stock_photo.jpg",
        },
        "len": 123
    }
}
```

<Info>
  MIME, also known as *internet media type*, is a standard identifier for specifying the structure and format of a document or file. It enables web browsers and other software systems to interpret and handle binary data appropriately when transmitting it over the internet. (See the official [MDN Web Docs > MIME types (IANA media types)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types "MDN Web Docs > MIME types (IANA media types)"))
</Info>

## Creating Attachments

There are two separate steps in the process of creating an attachment:

<Steps>
  <Step>
    Create the attachment in the Ditto store. ([Initiating ATTACHMENT Objects](#initiating-attachment-objects))
  </Step>

  <Step>
    Reference the returned attachment token in a document. ([Referencing Attachment Tokens](#referencing-attachment-tokens))
  </Step>
</Steps>

<Info>
  For a realworld usage scenario, see either the demo chat app for iOS or Android in the getditto > [demoapp-chat](https://github.com/getditto/demoapp-chat/tree/main "demoapp-chat") GitHub repository.
</Info>

### Initiating ATTACHMENT Objects

To create the ATTACHMENT object that encodes the actual contents of the file to store externally, call the `newAttachment` method on the `store` namespace.

<Accordion title="When creating an attachment...">
  <Note>
    When creating an attachment, provide a valid path to the large file or deeply embedded document you want to encode as an ATTACHMENT. Failure to do so may result in errors related to input-output (IO) operations.
  </Note>

  <Info>
    Storing an attachment in the local Ditto store (instead of externally) is currently only available in the Ditto SDK for JavaScript.
  </Info>

  <Info>
    To use a DQL type other than a REGISTER — the default data type in Ditto — you must explicitly specify the type in your query; otherwise, Ditto defaults to the REGISTER type for every document property.
  </Info>
</Accordion>

<CodeGroup>
  ```swift Swift theme={null}
  let newAttachment = try await ditto.store.newAttachment(path: filePath)
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.newAttachment(attachmentPathOrInputStream, attachmentMetadata).use { attachment ->
      ...
  }
  ```

  ```javascript JS theme={null}
  const newAttachment = await ditto.store.newAttachment(filePathOrData)
  ```

  ```typescript TS theme={null}
  import { Ditto, DittoAttachment } from '@dittolive/ditto'

  const newAttachment: DittoAttachment = await ditto.store.newAttachment(filePathOrData)
  ```

  ```java Java theme={null}
  // This try-with-resources block auto-closes the DittoAttachment
  try (DittoAttachment attachment =
          ditto.getStore().newAttachment(attachmentPathOrInputStream, attachmentMetadata)) {
      ...
  }
  ```

  ```csharp C# theme={null}
  using var newAttachment = await ditto.Store.NewAttachmentAsync(path: filePath);
  ```

  ```cpp C++ theme={null}
  auto attachment = ditto.get_store().new_attachment("/path/to/my/file.pdf");
  ```

  ```rust Rust theme={null}
  let file_path: std::path::PathBuf = // ...
  let metadata = Default::default();
  let attachment = ditto.store().new_attachment(&file_path, metadata).await?;
  ```

  ```dart Dart theme={null}
  final newAttachment = await ditto.store.newAttachment("/path/to/my/file.pdf");
  ```

  ```go Go theme={null}
  // The attachments API is not available for the current Go SDK Public Preview
  ```
</CodeGroup>

### Adding Optional Metadata

If desired, enclose extra information about the attachment, such as name and description. This metadata is useful for attachment fetching operations.

If you want to include information about the attachment, such as the attachment filename, as shown in the following snippet, a description, and other relevant details, enclose it in a metadata object as key-value pairs.

<CodeGroup>
  ```swift Swift theme={null}
  let metadata = ["name": "image.png"]
  let newAttachmentWithMetadata = try await ditto.store.newAttachment(path: filePath, metadata: metadata)
  ```

  ```kotlin Kotlin theme={null}
  val attachmentMetadata = mapOf("name" to attachmentFileName)
  ditto.store.newAttachment(attachmentPathOrInputStream, attachmentMetadata).use { attachment ->

  }
  ```

  ```javascript JS theme={null}
  const metadata = { "name": "image.png" }
  const newAttachment = await ditto.store.newAttachment(imageBytes, metadata)
  ```

  ```typescript TS theme={null}
  import { Ditto, DittoAttachment, AttachmentMetadata } from '@dittolive/ditto'

  const metadata: AttachmentMetadata = { name: "image.png" }
  const newAttachment: DittoAttachment = await ditto.store.newAttachment(imageBytes, metadata)
  ```

  ```java Java theme={null}
  import static com.ditto.java.serialization.DittoCborSerializable.buildDictionary;

  var metadata = buildDictionary()
      .put("name", "image.png")
      .build();

  // This try-with-resources block auto-closes the DittoAttachment
  try (DittoAttachment attachment =
          ditto.getStore().newAttachment(attachmentPathOrInputStream, metadata)) {
      ...
  }
  ```

  ```csharp C# theme={null}
  var metadata = new Dictionary<string, string>()
  {
      { "name", "image.png" }
  };
  using var newAttachmentWithMetadata = await Ditto.Store.NewAttachmentAsync(filePath, metadata);
  ```

  ```cpp C++ theme={null}
  auto attachment = ditto.get_store().new_attachment("/path/to/my/file.pdf", {
      {"name", "image.png"}
  });
  ```

  ```rust Rust theme={null}
  let metadata = std::collections::HashMap::from_iter([
      ("name".into(), "image.png".into()),
  ]);

  let file_path: std::path::PathBuf = // ...
  let attachment = ditto.store().new_attachment(&file_path, metadata).await?;
  ```

  ```dart Dart theme={null}
  final attachment = await ditto.store.newAttachment(
    "/path/to/my/file.pdf",
    AttachmentMetadata({"name": "image.png"}),
  );
  ```

  ```go Go theme={null}
  // The attachments API is not available for the current Go SDK Public Preview
  ```
</CodeGroup>

For example, in the following snippet, the metadata property encapsulates the name of the attachment, as well as its description:

<CodeGroup>
  ```swift Swift theme={null}
  let metadata = ["name": "japan.png", "description": "This is an image of a sunset in Japan."];
  let newAttachmentWithMetadata = try await ditto.store.newAttachment(path: filePath, metadata: metadata)
  ```

  ```kotlin Kotlin theme={null}
  val attachmentMetadata = mapOf(
      "name" to "japan.png",
      "description" to "This is an image of a sunset in Japan."
  )

  ditto.store.newAttachment(attachmentPathOrInputStream, attachmentMetadata).use { attachment ->

  }
  ```

  ```javascript JS theme={null}
  const metadata = { "name": "japan.png", "description": "This is an image of a sunset in Japan." }
  const newAttachment = await ditto.store.newAttachment(imageBytes, metadata)
  ```

  ```typescript TS theme={null}
  import { Ditto, DittoAttachment, AttachmentMetadata } from '@dittolive/ditto'

  const metadata: AttachmentMetadata = {
    name: "japan.png",
    description: "This is an image of a sunset in Japan."
  }
  const newAttachment: DittoAttachment = await ditto.store.newAttachment(imageBytes, metadata)
  ```

  ```java Java theme={null}
  import static com.ditto.java.serialization.DittoCborSerializable.buildDictionary;

  var metadata = buildDictionary()
      .put("name", "japan.png")
      .put("description", "This is an image of a sunset in Japan.")
      .build();

  // This try-with-resources block auto-closes the DittoAttachment
  try (DittoAttachment attachment =
          ditto.getStore().newAttachment(attachmentPathOrInputStream, metadata)) {
      ...
  }
  ```

  ```csharp C# theme={null}
  var metadata = new Dictionary<string, string>()
  {
  { "name", "japan.png" },
  { "description", "This is an image of a sunset in Japan." }
  };
  using var newAttachmentWithMetadata = await Ditto.Store.NewAttachmentAsync(filePath, metadata);
  ```

  ```cpp C++ theme={null}
  std::map<std::string, std::string> metadata;
  args["name"] = "japan.png";
  args["description"] = "This is an image of a sunset in Japan."

  auto attachment = ditto.get_store().new_attachment("/path/to/my/file.pdf", medatata);
  ```

  ```rust Rust theme={null}
  let metadata = std::collections::HashMap::from_iter([
      ("name".into(), "japan.png".into()),
      ("description".into(), "This is an image of a sunset in Japan.".into()),
  ]);

  let file_path: std::path::PathBuf = // ...
  let attachment = ditto.store().new_attachment(&file_path, metadata).await?;
  ```

  ```dart Dart theme={null}
  final attachment = await ditto.store.newAttachment(
    filePath,
    {
      "name": "japan.png",
      "description": "This is an image of a sunset in Japan.",
    },
  );
  ```

  ```go Go theme={null}
  // The attachments API is not available for the current Go SDK Public Preview
  ```
</CodeGroup>

### Inserting Attachments in a Document

After creating and storing a new attachment object in Ditto, you receive an *attachment token* as part of the response.

<Accordion title="An attachment token is...">
  An attachment token is a pointer that references its associated ATTACHMENT object. This token is how you interact with the attachment, such as when fetching or linking it with a document.
</Accordion>

The following snippet demonstrates how to create a new document object containing a new attachment, and then insert it into the cars collection:

<CodeGroup>
  ```swift Swift theme={null}
  // First, create the attachment
  let metadata = ["name": "image.png"]
  let attachment = try await ditto.store.newAttachment(path: filePath, metadata: metadata)

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  let newDocument: [String: Any] = ["_id": "123", "my_attachment": attachment]

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  let result = await ditto.store.execute(
    query: "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:newDocument)",
    arguments: ["newDocument": newDocument]);
  ```

  ```kotlin Kotlin theme={null}
  // First, create the attachment
  val attachmentMetadata = mapOf("name" to "image.png")
  ditto.store.newAttachment(attachmentPathOrInputStream, attachmentMetadata).use { attachment ->
    // Then, create a new document object and store the attachment on the `my_attachment` field.
    val document = mapOf("_id" to "123", "my_attachment" to attachment)

    // Finally, insert the new document into the collection.
    // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
    // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
    val insertQuery = "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:document)"
    val insertArguments = mapOf("document" to document)
    ditto.store.execute(insertQuery, insertArguments)
  }
  ```

  ```javascript JS theme={null}
  // First, create the attachment
  const metadata = { "name": "image.png" }
  const attachment = await ditto.store.newAttachment(imageBytes, metadata)

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  const newDocument = { "_id": "123", my_attachment: attachment }

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  await ditto.store.execute(`
    INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:newDocument)`,
    { newDocument }
  );
  ```

  ```typescript TS theme={null}
  import { Ditto, DittoAttachment, QueryResult, AttachmentMetadata } from '@dittolive/ditto'

  interface DocumentWithAttachment {
    _id: string
    my_attachment: DittoAttachment
  }

  // First, create the attachment
  const metadata: AttachmentMetadata = { name: "image.png" }
  const attachment: DittoAttachment = await ditto.store.newAttachment(imageBytes, metadata)

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  const newDocument: DocumentWithAttachment = { _id: "123", my_attachment: attachment }

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  const result: QueryResult = await ditto.store.execute(
    `INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:newDocument)`,
    { newDocument }
  )
  ```

  ```java Java theme={null}
  import static com.ditto.java.serialization.DittoCborSerializable.buildDictionary;

  // First, create the attachment
  var metadata = buildDictionary()
      .put("name", "image.png")
      .build();

  // This try-with-resources block auto-closes the DittoAttachment
  try (DittoAttachment attachment =
          ditto.getStore().newAttachment(attachmentPathOrInputStream, metadata)) {
      // Then, create a new document object and store the attachment on the `my_attachment` field.
      var document = buildDictionary()
          .put("_id", "123")
          .put("my_attachment", attachment)
          .build();

      // Finally, insert the new document into the collection.
      // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
      // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
      // This try-with-resources block auto-closes the DittoQueryResult
      try (DittoQueryResult result = ditto.getStore().execute(
            "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:document)",
            buildDictionary().put("document", document).build()
          ).toCompletableFuture().join()) {
          ...
      }
  }
  ```

  ```csharp C# theme={null}
  // First, create the attachment
  using var attachment = await ditto.Store.NewAttachmentAsync(path: filePath);

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  var document = new Dictionary<string, object>();
  document.Add("_id", "123");
  document.Add("my_attachment", attachment);

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  await ditto.Store.ExecuteAsync(
    "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:document)",
    new { "document" = document });
  ```

  ```cpp C++ theme={null}
  // First, create the attachment
  auto attachment = ditto.get_store().new_attachment("/path/to/my/file.pdf");

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  struct Document {
    std::string _id;
    Attachment my_attachment;
  }

  std::map<std::string, Document>> args;
  args["document"] = {"123", "attachment"};

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  auto result = ditto.get_store().execute(
    "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:document)",
    args);
  ```

  ```rust Rust theme={null}
  // First, create the attachment
  let file_path: std::path::PathBuf = // ...
  let metadata = Default::default();
  let attachment = ditto.store().new_attachment(&file_path, metadata).await?;

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  let args = serde_json::json!({
    "document": {
        "_id": "123"
        "my_attachment": attachment
    }
  })

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  let result = ditto
    .store()
    .execute((
      "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:document)",
      args,
    )).await?;
  ```

  ```dart Dart theme={null}
  // First, create the attachment
  final newAttachment = await ditto.store.newAttachment("/path/to/my/file.pdf");

  // Then, create a new document object and store the attachment on the `my_attachment` field.
  final document = {
    "_id": "123",
    "my_attachment": attachment,
  };

  // Finally, insert the new document into the collection.
  // Note that the `my_attachment` field needs to be declared as an `ATTACHMENT` data type.
  // When explicitly declaring types for your fields, the COLLECTION keyword is mandatory.
  final query = "INSERT INTO COLLECTION docs (my_attachment ATTACHMENT) VALUES (:document)";
  final arguments = {"document": document};

  final result = await ditto.store.execute(query, arguments);
  ```

  ```go Go theme={null}
  // The attachments API is not available for the current Go SDK Public Preview
  ```
</CodeGroup>

## Fetching attachments

To fetch an attachment, you must first query the document containing the
attachment token. The token is a pointer that references the full attachment
stored externally.

The following snippet demonstrates how to fetch an attachment from a document
with the ID `123` in the cars collection. The attachment token is stored in the
`my_attachment` field of the document.

<Note>
  The attachment fetcher needs to be kept alive until the attachment is
  successfully fetched.
</Note>

<Accordion title="Syncing with Big Peers">
  Big Peers might have a document with an attachment token but no access to the
  actual attachment data. This can happen in a mesh of small peers that replicate
  documents but don’t fetch attachments. If a small peer creates an attachment and
  isn’t connected to a Big Peer, the document will only spread through the mesh if
  other peers explicitly fetch the attachment.
</Accordion>

<CodeGroup>
  ```swift Swift theme={null}
  let result = try await ditto.store.execute(
      query: """
          SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
          WHERE _id = "123"
      """,
  )

  let token = result.items.first?.value["my_attachment"] as? [String: Any]
  if let token = token {
    let fetcher = try await ditto.store.fetchAttachment(token) { event in
        switch (event) {
            case .completed(let fetchedAttachment):
            case .progress:
            case .deleted:
        }
      }
  }
  ```

  ```kotlin Kotlin theme={null}
  ditto.store.execute(
      query = """
  	    SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
  	    WHERE _id = "123"
      """,
  ).use { result ->
    val token = result.items.first().value["my_attachment"] as Map<String, Any>

    val fetcher = ditto.store.fetchAttachment(token) {
      when (it) {
        is DittoAttachmentFetchEvent.Completed -> {
        }
        is DittoAttachmentFetchEvent.Progress -> {
        }
        is DittoAttachmentFetchEvent.Deleted -> {
        }
      }
    }
  }
  ```

  ```javascript JS theme={null}
  const result = await ditto.store.execute(`
    SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
      WHERE _id = "123"
  `)
  const token = result.items[0].value["my_attachment"]
  const fetcher = await ditto.store.fetchAttachment(token, (event) => {
    if (event.type === 'Completed') {
      // Handle the completed attachment
    	attachment = event.attachment;
    } else if (event.type === 'Progress') {
      // Handle progress updates
    } else if (event.type === 'Deleted') {
      // Handle deletion of the attachment
    }
  });
  ```

  ```typescript TS theme={null}
  import { Ditto, QueryResult, AttachmentFetchEvent, AttachmentFetcher, DittoAttachment } from '@dittolive/ditto'

  interface AttachmentToken {
    id: string
    len: number
    metadata: Record<string, string>
  }

  const result: QueryResult = await ditto.store.execute(`
    SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
      WHERE _id = "123"
  `)
  const token: AttachmentToken = result.items[0].value["my_attachment"] as AttachmentToken
  const fetcher: AttachmentFetcher = await ditto.store.fetchAttachment(token, (event: AttachmentFetchEvent) => {
    if (event.type === 'Completed') {
      // Handle the completed attachment
      const attachment: DittoAttachment = event.attachment
    } else if (event.type === 'Progress') {
      // Handle progress updates
    } else if (event.type === 'Deleted') {
      // Handle deletion of the attachment
    }
  })
  ```

  ```java Java theme={null}
  import static com.ditto.java.serialization.DittoCborSerializable.buildDictionary;
  import com.ditto.java.serialization.DittoCborSerializable;

  // This try-with-resources block auto-closes the DittoQueryResult
  try (DittoQueryResult result = ditto.getStore().execute(
      "SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT) WHERE _id = :id",
      buildDictionary().put("id", "123").build()
  ).toCompletableFuture().join()) {
      DittoQueryResultItem item = result.getItems().get(0);
      if (item != null) {
          DittoCborSerializable.Dictionary token = item.getValue().get("my_attachment").getDictionary();

          var completionStage = ditto.getStore().fetchAttachment(token,
              (downloadedBytes, totalBytes) -> {
                  // Download progress
                  System.out.println("Downloaded " + downloadedBytes + " bytes out of " + totalBytes + " bytes");
                  return CompletableFuture.completedStage(null);
              }
          );
          DittoAttachmentFetchResult attachmentResult = completionStage.toCompletableFuture.join();
          if (attachmentResult == DittoAttachmentFetchResult.Completed)
              try (DittoAttachment attachment = attachmentResult.getAttachment() {
                  // Process the fetched attachment data
              }
          }
      }
  }
  ```

  ```csharp C# theme={null}
  using var result = await ditto.Store.ExecuteAsync(
      query: @"
          SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
          WHERE _id = '123'
      "
  );
  var fetcher = await ditto.Store.FetchAttachment(token, fetchEvent =>
  {
      switch (fetchEvent)
      {
          case DittoAttachmentFetchEvent.Completed e:
              // Handle the completed attachment
              break;
          case DittoAttachmentFetchEvent.Progress:
              // Handle progress updates
              break;
          case DittoAttachmentFetchEvent.Deleted:
              // Handle deletion of the attachment
              break;
      }
  });
  ```

  ```cpp C++ theme={null}
  auto result = ditto.get_store().execute(
      "SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT) WHERE _id = :id",
      {{"id", "123"}}
  );
  auto token = result.get_items().get_first().value["my_attachment"];
  auto fetcher = ditto.get_store().fetch_attachment(token);
  fetcher.on_completed([](DittoAttachment attachment) {
      // Handle the completed attachment
  });
  fetcher.on_progress([](size_t bytes_transferred, size_t total_bytes) {
      // Handle progress updates
  });
  fetcher.on_deleted([]() {
      // Handle deletion of the attachment
  });
  ```

  ```rust Rust theme={null}
  let result = ditto
      .store()
      .execute(r#"
        SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
        WHERE _id = "123"
      "#)
      .await?;

  let token = result.get_item(0).unwrap().value["my_attachment"].as_object().unwrap();

  let fetcher = ditto.store().fetch_attachment(token, move |event| match event {
      DittoAttachmentFetchEvent::Completed {
          attachment,
      } => {
          // Handle the completed attachment
      }
      DittoAttachmentFetchEvent::Progress {
          downloaded_bytes,
          total_bytes,
      } => {
          // Handle progress updates
      }
      DittoAttachmentFetchEvent::Deleted => {
          // Handle deletion of the attachment
      }
  })
  .unwrap();
  ```

  ```dart Dart theme={null}
  final result = await ditto.store.execute('''
      SELECT * FROM COLLECTION cars (my_attachment ATTACHMENT)
      WHERE _id = "123"
  ''');
  final token = result.items.first.value["my_attachment"] as Map<String, dynamic>;
  final attachment = await ditto.store.fetchAttachment(token);
  final fetcher = ditto.store.fetchAttachment(token, (event) {
    switch (event) {
      case AttachmentFetchEventCompleted completed:
        // Handle the completed attachment
      case AttachmentFetchEventProgress progress:
        // Handle progress updates
      case AttachmentFetchEventDeleted deleted:
        // Handle deletion of the attachment
    }
  });
  ```

  ```go Go theme={null}
  // The attachments API is not available for the current Go SDK Public Preview
  ```
</CodeGroup>

## Updating Attachment Tokens

Once an `ATTACHMENT` object is created and linked to a document, it becomes immutable, meaning its contents cannot be changed.

However, you can *indirectly* update attachments by performing an `UPDATE` operation on the document to replace the existing *attachment token* with a different one. The attachment token is the reference pointer linking the document to the attachment's binary data stored separately.

<Info>
  Before you can access an attachment in your app, you must actively fetch it, unlike a document, which is always readily accessible. For instructions, see [READ](./read).
</Info>

<CodeGroup>
  ```swift Swift theme={null}

  // Updating a Ditto Document with an attachment
  // Update the document "123" with a new attachment
  try await ditto.store.execute(
      query: """
      UPDATE COLLECTION cars (my_attachment ATTACHMENT)
      SET my_attachment = :newAttachment
      WHERE _id = "123"
      """,
      arguments: ["newAttachment": newAttachment]
  )
  ```

  ```kotlin Kotlin theme={null}
  // Updating a Ditto Document with an attachment
  // Fetch the document "123" with a new attachment
  val query = "SELECT * FROM COLLECTION cars WHERE _id = :id"
  val arguments = mapOf("id" to "123") // Assuming the document ID is "123"
  ditto.store.execute(query, arguments).use { selectQueryResult ->
    // Get the existing document
    val existingDocument = selectQueryResult.items.firstOrNull()
    if (existingDocument != null) {
        // Modify the document, including updating the attachment reference
        val updatedDocument = existingDocument.value.toMutableMap()
        updatedDocument["my_attachment"] = newAttachment // Assuming `newAttachment` is the updated attachment

        // Save the updated document back to the collection
        val updateQuery = "UPDATE COLLECTION cars VALUES (:document) WHERE _id = :id"
        val updateArguments = mapOf("document" to updatedDocument, "id" to "123") // Assuming the document ID is "123"
        ditto.store.execute(updateQuery, updateArguments)
    } else {
        println("Document not found.")
    }
  }
  ```

  ```javascript JS theme={null}
  // Updating a Ditto Document with an attachment
  // Update the document "123" with a new attachment
  await ditto.store.execute(`
    UPDATE COLLECTION cars (my_attachment ATTACHMENT)
      SET my_attachment = :newAttachment
      WHERE _id = "123"`,
    { newAttachment }
  );
  ```

  ```typescript TS theme={null}
  import { Ditto, DittoAttachment, QueryResult } from '@dittolive/ditto'

  // Updating a Ditto Document with an attachment
  // Update the document "123" with a new attachment
  const newAttachment: DittoAttachment = await ditto.store.newAttachment(newFilePath)

  const result: QueryResult = await ditto.store.execute(
    `UPDATE COLLECTION cars (my_attachment ATTACHMENT)
      SET my_attachment = :newAttachment
      WHERE _id = "123"`,
    { newAttachment }
  )
  ```

  ````java Java theme={null}
  import static com.ditto.java.serialization.DittoCborSerializable.buildDictionary;
  import com.ditto.java.serialization.DittoCborSerializable;

  // Updating a Ditto Document with an attachment
  // Fetch the document "123" with a new attachment
  String query = "SELECT * FROM COLLECTION cars WHERE _id = :id";

  // This try-with-resources block auto-closes the DittoQueryResult
  try (
      DittoQueryResult result = ditto.getStore.execute(
          query,
          buildDictionary().put("id", "123").build()
      ).toCompletableFuture().join()
  ) {
      // Get the existing document
      DittoQueryResultItem existingDocument = result.getItems().getFirst();
      if (existingDocument != null) {
          // Modify the document, including updating the attachment reference
          DittoCborSerializable.Dictionary existingDoc = existingDocument.getValue();

          // Create updated document with new attachment
          var updatedDocument = buildDictionary()
              .put("_id", existingDoc.get("_id"))
              .put("my_attachment", newAttachment) // Assuming `newAttachment` is the updated attachment
              .build();

          // Save the updated document back to the collection
          String updateQuery = "UPDATE COLLECTION cars VALUES (:document) WHERE _id = :id";
          var updateArguments = buildDictionary()
              .put("document", updatedDocument)
              .put("id", "123")
              .build();

          ditto.getStore.execute(updateQuery, updateArguments)
              .toCompletableFuture().join()
              .close(); // DittoQueryResult from execute() MUST always be explicitly closed
      } else {
          System.out.println("Document not found.");
      }
  }

  ```csharp C#
  // Updating a Ditto Document with an attachment
  // Update the document "123" with a new attachment
  await ditto.Store.ExecuteAsync(
      query: @"
           UPDATE COLLECTION cars (my_attachment ATTACHMENT)
           SET my_attachment = :newAttachment
           WHERE _id = '123'
           ",
      arguments: new Dictionary<string, object>() { { "newAttachment", newAttachment } }
  );

  ````

  ```rust Rust theme={null}
  // Updating a ditto document with an attachment
      // (update the document "123" with a new attachment)
      peer_a
          .store()
          .execute(
              r#"
                  UPDATE COLLECTION cars (my_attachment ATTACHMENT)
                  SET my_attachment = :new_attachment
                  WHERE _id = "123"
              "#,
              json!({
                  "new_attachment": new_attachment,
              }),
          )
          .await?;

      wait_for_id_to_be_present_in(peer_b, "123").await?;
  ```

  ```dart Dart theme={null}
  await ditto.store.execute('''
    UPDATE COLLECTION cars (my_attachment ATTACHMENT)
      SET my_attachment = :newAttachment
      WHERE _id = "123"''',
    arguments: { "newAttachment": newAttachment },
  );
  ```

  ```go Go theme={null}
  // The attachments API is not available for the current Go SDK Public Preview
  ```
</CodeGroup>

## Deleting Attachments

Unlike documents, attachments cannot be explicitly deleted on their own. Instead, you modify the document containing the attachment token referencing it.

Attachment data stored within the Small Peer Ditto store is automatically garbage collected on a 10-minute cadence when no longer referenced. Currently, attachments can only be deleted by way of garbage collection.

The following table provides an overview of the various ways you can indirectly delete attachments:

| **Approach** | **Description**                                                                              |
| ------------ | -------------------------------------------------------------------------------------------- |
| `UPDATE`     | Update the document to remove the associated attachment token.                               |
| `EVICT`      | Delete the entire document, including the associated attachment token, from the Ditto store. |

The storage mechanism Small Peers use to store data, including blob data, depends on the platform:

* If running in the browser or a server-based system, data is stored in its Random Access Memory (RAM).
* If running on a mobile device like an iPhone, data is stored on its local filesystem.

## Blob Store

A JSON blob stores binary data representing any file type. For example, it can store image files like JPEG, PNG, and TIFF; video files such as MP4; audio files, including MP3 and WAV; document files like PDF; and more.

The metadata is stored in the document, while the blob is stored in a dedicated location independent of documents. The following table provides an overview of blob storage locations:

| **Type**       | **Blob Store Location**                                                                                                                                                                       |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ditto Edge SDK | - If running in the browser or a server-based system, in-memory storage. Specifically, within Random Access Memory (RAM). - If running on a mobile device like an iPhone, filesystem storage. |
| Ditto Server   | Cloud object storage service: Amazon WebServices (AWS) Simple Storage Service (S3).                                                                                                           |

### Organizing by ID

Ditto organizes and stores blob data using the content-addressable network (*CAN*) paradigm. CAN is a distributed network architecture widely implemented in decentralized systems like Ditto's where scalability, efficiency, and fault tolerance are essential requirements. (See the official [*Content-Addressable Network*](https://en.wikipedia.org/wiki/Content-addressable_network "Content-Addressable Network") Wikipedia article.)

In more straightforward terms, Ditto organizes and accesses attachment data by the cryptographic *hash table* of its contents — for instance, attachment ID — rather than its physical location. A hash table is a data structure that stores key-value pairs.

By adhering to the CAN framework for attachment management, Ditto stores and retrieves data efficiently. For instance:

* *Deduplication* — If the same blob is created multiple times, Ditto stores only one copy. Similarly, if an attachment is added to multiple documents, they all reference the same blob.
* *Conflict-free sync* — Blobs are stored based on the cryptographic hash of their contents, ensuring that conflicts are effectively impossible.
* *Decentralized* — No central server is required to manage attachments. They can be created and accessed concurrently throughout the network, even with intermittent connectivity.

### Purging Blob Data

Blob storage is managed internally using a reference-counting process known as *garbage collection*.

The garbage collection process running periodically in the background of Small Peers frees up space by removing blobs that no longer have any references — once an attachment has no remaining references, its blob is considered safe to remove, and the garbage collection process clears it from the device.

### Syncing ATTACHMENT Objects

The two components comprising an ATTACHMENT object — metadata stored internally within a document and blob data storing the actual binary data of your attachment — sync in different ways:

* Metadata syncs just as any other document.
* Blob data syncs with an entirely different protocol than documents; one that is asynchronous and built on top of Ditto's rainbow connection technology and optimizes for large binary data transfer. (See [Mesh Networking](/key-concepts/mesh-networking))

Attachment blob sync being asynchronous provides three key advantages:

* Unblocking fetching operations so the document continues syncing without interruption when retrieving an attachment.
* Ditto multiplexes between these independent activities to balance needs for low latency and high throughput at the edge.
* Blob sync is resilient, so if connectivity is interrupted while transferring an attachment, progress is not lost. Once restored or if another peer possesses the attachment, Ditto resumes the transfer from the point of interruption.

Blob sync remains performant — Ditto uses data structures like *Bloom filters* to scale efficiently and remain light on system resources.

Bloom filters are randomized data structures designed to represent sets in a compressed form, making them highly effective in peer-to-peer environments where concerns such as memory usage and accuracy are prevalent. (See the official [*Bloom filter*](https://en.wikipedia.org/wiki/Bloom_filter "Bloom filter") Wikipedia article.)

### Garbage Collection

When an attachment token stored on a Small Peer end-user device is unused, meaning there are no remaining references to it, the *garbage collection* process running periodically in the background automatically deletes it to free up space.

Garbage collection is a reference-counting process that only runs on Small Peer devices (not on the Ditto Server). It involves scanning for objects, including blob objects, that are no longer needed or relevant and are therefore safe to delete from its device.

Ditto Server stores its data in Ditto's third-party cloud object storage service provider, Amazon WebServices (AWS) Simple Storage Service (S3). For more information, see the official [*Amazon Simple Storage Service Documentation*](https://docs.aws.amazon.com/s3/ "Amazon Simple Storage Service Documentation").

### Lazy-Load Retrieval Pattern

Since the blob representing your large file is stored outside the local Ditto store, fetch it only when needed. This on-demand retrieval pattern, known as *lazy loading*, reduces resource usage on end-user devices.

For a realworld usage scenario, see either the demo chat app for iOS or Android in the getditto > [demoapp-chat](https://github.com/getditto/demoapp-chat/tree/main "demoapp-chat") GitHub repository. For instance, in the [iOS demo chat app](https://github.com/getditto/demoapp-chat/tree/main/iOS "iOS demo chat app"), you can see a savvy implementation of ATTACHMENT with a full-resolution avatar image from a collection named User.
