IBM Cloud Docs
How to use attachments

How to use attachments

Another way to store data is to use attachments. Attachments are Binary Large OBject (BLOB) files that are included within documents.

It's a good idea to keep attachments small in size and number because attachments can impact performance.

The BLOB is stored in the _attachments component of the document. The BLOB holds data that includes the following information:

  • The attachment name
  • The type of the attachment
  • The actual content

Examples of BLOBs would be images and multimedia.

If you include the attachment as an inline component of the overall JSON, the attachment content is represented by using BASE64 form.

The content type corresponds to a MIME type. For example, if you want to attach a .jpg image file to a document, you specify the attachment MIME type as image/jpeg.

Attachments aren't permitted on documents in _replicator or _users databases.

Create or update

To create a new attachment at the same time as creating a new document, include the attachment as an inline component of the JSON content.

To create a new attachment on an existing document, or to update an attachment on a document, make a PUT request with the document's most recent _rev to https://$ACCOUNT.cloudant.com/$DATABASE/$DOCUMENT_ID/$ATTACHMENT. The attachment's content type must be specified by using the Content-Type header. The $ATTACHMENT value is the name by which the attachment is associated with the document.

You can create more than one attachment for a document by ensuring that the $ATTACHMENT value for each attachment is unique within the document.

See the following example for creating or updating an attachment by using HTTP:

PUT /$DATABASE/$DOCUMENT_ID/$ATTACHMENT?rev=$REV HTTP/1.1
Content-Type: $$ATTACHMENT_MIME_TYPE

See the following example for creating or updating an attachment:

curl -H "Authorization: Bearer $API_BEARER_TOKEN" -X PUT "$SERVICE_URL/products/small-appliances:100001/product_details.txt" -H "Content-Type: text/plain" --data 'This appliance includes...'
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.DocumentResult;
import com.ibm.cloud.cloudant.v1.model.PutAttachmentOptions;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

Cloudant service = Cloudant.newInstance();

String detailedDescription = "This appliance includes...";

InputStream detailedDescriptionStream =
    new ByteArrayInputStream(detailedDescription
        .getBytes(StandardCharsets.UTF_8));

PutAttachmentOptions attachmentOptions =
    new PutAttachmentOptions.Builder()
        .db("products")
        .docId("small-appliances:100001")
        .attachmentName("product_details.txt")
        .attachment(detailedDescriptionStream)
        .contentType("text/plain")
        .build();

DocumentResult response =
    service.putAttachment(attachmentOptions).execute()
        .getResult();

System.out.println(response);
const { CloudantV1 } = require('@ibm-cloud/cloudant');

const service = CloudantV1.newInstance({});

const stream = new Readable();
stream.push('This appliance includes...');
stream.push(null);

service.putAttachment({
  db: 'products',
  docId: 'small-appliances:100001',
  attachmentName: 'product_details.txt',
  attachment: stream,
  contentType: 'text/plain'
}).then(response => {
  console.log(response.result);
});
from ibmcloudant.cloudant_v1 import CloudantV1

service = CloudantV1.new_instance()

detailed_description = "This appliance includes..."
response = service.put_attachment(
  db='products',
  doc_id='small-appliances:100001',
  attachment_name='product_details.txt',
  attachment=detailed_description,
  content_type='text/plain'
).get_result()

print(response)
putAttachmentOptions := service.NewPutAttachmentOptions(
  "products",
  "small-appliances:100001",
  "product_details.txt",
  ioutil.NopCloser(
    bytes.NewReader([]byte("This appliance includes...")),
  ),
  "text/plain",
)

documentResult, response, err := service.PutAttachment(putAttachmentOptions)
if err != nil {
  panic(err)
}

b, _ := json.MarshalIndent(documentResult, "", "  ")
fmt.Println(string(b))

The previous Go example requires the following import block:

import (
   "encoding/json"
   "fmt"
   "io/ioutil"
   "github.com/IBM/cloudant-go-sdk/cloudantv1"
)

All Go examples require the service object to be initialized. For more information, see the API documentation Authentication section for examples.

The response includes the document ID and the new document revision.

Attachments don't have their own revisions. Instead, when you update or create an attachment, the revision of the document it's attached to changes.

See the following example response with the document ID and new revision:

{
	"id" : "FishStew",
	"ok" : true,
	"rev" : "9-247bb19a41bfd9bfdaf5ee6e2e05be74"
}

Read

To retrieve an attachment, make a GET request to https://$ACCOUNT.cloudant.com/$DATABASE/$DOCUMENT_ID/$ATTACHMENT. The body of the response is the raw content of the attachment.

See the following example of reading an attachment by using HTTP:

GET /$DATABASE/$DOCUMENT_ID/$ATTACHMENT HTTP/1.1

See the following example of reading an attachment:

curl -H "Authorization: Bearer $API_BEARER_TOKEN" -X GET "$SERVICE_URL/products/small-appliances:100001/product_details.txt"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.GetAttachmentOptions;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

Cloudant service = Cloudant.newInstance();

GetAttachmentOptions attachmentOptions =
    new GetAttachmentOptions.Builder()
        .db("products")
        .docId("small-appliances:100001")
        .attachmentName("product_details.txt")
        .build();

InputStream streamResult =
    service.getAttachment(attachmentOptions).execute()
        .getResult();

String response =
    new BufferedReader(new InputStreamReader(streamResult))
        .lines().collect(Collectors.joining("\n"));

System.out.println(response);
const { CloudantV1 } = require('@ibm-cloud/cloudant');

const service = CloudantV1.newInstance({});

service.getAttachment({
  db: 'products',
  docId: 'small-appliances:100001',
  attachmentName: 'product_details.txt'
}).then(response => {
  let attachment = response.result as Readable;
  attachment.pipe(process.stdout);
});
from ibmcloudant.cloudant_v1 import CloudantV1

service = CloudantV1.new_instance()

response_attachment = service.get_attachment(
  db='products',
  doc_id='small-appliances:100001',
  attachment_name='product_details.txt'
).get_result().content

print(response_attachment)
getAttachmentOptions := service.NewGetAttachmentOptions(
  "products",
  "small-appliances:100001",
  "product_details.txt",
)

result, response, err := service.GetAttachment(getAttachmentOptions)
if err != nil {
  panic(err)
}

data, _ := ioutil.ReadAll(result)
fmt.Println("\n", string(data))

The previous Go example requires the following import block:

import (
   "fmt"
   "io/ioutil"
   "github.com/IBM/cloudant-go-sdk/cloudantv1"
)

All Go examples require the service object to be initialized. For more information, see the API documentation's Authentication section for examples.

Delete an attachment

To delete an attachment, make a DELETE request with the document's most recent _rev to https://$ACCOUNT.cloudant.com/$DATABASE/$DOCUMENT_ID/$ATTACHMENT. If you don't supply the most recent _rev, the response is a 409 error.

See the following example of deleting an attachment by using HTTP:

DELETE /$DATABASE/$DOCUMENT_ID/$ATTACHMENT?rev=$REV HTTP/1.1

See the following example of deleting an attachment:

curl -H "Authorization: Bearer $API_BEARER_TOKEN" -X DELETE "$SERVICE_URL/products/small-appliances:100001/product_details.txt?rev=4-1a0d1cd6f40472509e9aac646183736a"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.DeleteAttachmentOptions;
import com.ibm.cloud.cloudant.v1.model.DocumentResult;

Cloudant service = Cloudant.newInstance();

DeleteAttachmentOptions attachmentOptions =
    new DeleteAttachmentOptions.Builder()
        .db("products")
        .docId("small-appliances:100001")
        .attachmentName("product_details.txt")
        .rev("4-1a0d1cd6f40472509e9aac646183736a")
        .build();

DocumentResult response =
    service.deleteAttachment(attachmentOptions).execute()
        .getResult();

System.out.println(response);
const { CloudantV1 } = require('@ibm-cloud/cloudant');

const service = CloudantV1.newInstance({});

service.deleteAttachment({
  db: 'products',
  docId: 'small-appliances:100001',
  attachmentName: 'product_details.txt',
  rev: '4-1a0d1cd6f40472509e9aac646183736a'
}).then(response => {
  console.log(response.result);
});
from ibmcloudant.cloudant_v1 import CloudantV1

service = CloudantV1.new_instance()

response = service.delete_attachment(
  db='products',
  doc_id='small-appliances:100001',
  attachment_name='product_details.txt',
  rev='4-1a0d1cd6f40472509e9aac646183736a'
).get_result()

print(response)
deleteAttachmentOptions := service.NewDeleteAttachmentOptions(
  "products",
  "small-appliances:100001",
  "product_details.txt",
)
deleteAttachmentOptions.SetRev("4-1a0d1cd6f40472509e9aac646183736a")

documentResult, response, err := service.DeleteAttachment(deleteAttachmentOptions)
if err != nil {
  panic(err)
}

b, _ := json.MarshalIndent(documentResult, "", "  ")
fmt.Println(string(b))

The previous Go example requires the following import block:

import (
   "encoding/json"
   "fmt"
   "github.com/IBM/cloudant-go-sdk/cloudantv1"
)

All Go examples require the service object to be initialized. For more information, see the API documentation's Authentication section for examples.

If the deletion is successful, the response includes "ok": true, and the ID and new revision of the document.

See the following example response after a successful delete of an attachment:

{
	"ok": true,
	"id": "DocID",
	"rev": "3-aedfb06537c1d77a087eb295571f7fc9"
}

Inline

Inline attachments are attachments that are included as part of the JSON content. The content must be provided by using BASE64 representation, as shown in the example.

A full list of media types is available in the media types article.

See the following example JSON document that includes an inline attachment of a jpeg image:

{
	"_id":"document_with_attachment",
	"_attachments":
	{
		"name_of_attachment": {
			"content_type":"image/jpeg",
			"data": "iVBORw0KGgoAA... ...AASUVORK5CYII="
		}
	}
}

Performance considerations

While document attachments are useful, they do have implications for application performance. In particular, having too many attachments can have an adverse performance impact during replication.

For example, if your application requires storage for multiple images as attachments or includes large images, you must use an alternative BLOB storage mechanism to store the images. You might then use IBM Cloudant to keep the image metadata, such as URLs to the BLOB store.

You might find it helpful to do performance testing for your specific application to determine which approach works best for your circumstances.