Attachments
Attachments are binary data files that can be attachment to an RxDocument, like a file that is attached to an email.
Using attachments instead of adding the data to the normal document, ensures that you still have a good performance when querying and writing documents, even when a big amount of data, like an image file has to be stored.
- You can store string, binary files, images and whatever you want side by side with your documents.
- Deleted documents automatically loose all their attachments data.
- Not all replication plugins support the replication of attachments.
- Attachments can be stored encrypted.
Internally, attachment data is stored as Blob objects. Blob is the canonical internal type because it is immutable, carries MIME type metadata via Blob.type, provides synchronous size via Blob.size, and is structured-cloneable (works with Worker/Electron postMessage and IndexedDB). Conversion to ArrayBuffer only happens at system boundaries that require it: encryption (Web Crypto), compression (CompressionStream), digest hashing, and WebSocket serialization.
Add the attachments pluginโ
To enable the attachments, you have to add the attachments plugin.
import { addRxPlugin } from 'rxdb';
import { RxDBAttachmentsPlugin } from 'rxdb/plugins/attachments';
addRxPlugin(RxDBAttachmentsPlugin);Enable attachments in the schemaโ
Before you can use attachments, you have to ensure that the attachments-object is set in the schema of your RxCollection.
const mySchema = {
version: 0,
type: 'object',
properties: {
// .
// .
// .
},
attachments: {
encrypted: true // if true, the attachment-data will be encrypted with the db-password
}
};
const myCollection = await myDatabase.addCollections({
humans: {
schema: mySchema
}
});putAttachment()โ
Adds an attachment to a RxDocument. Returns a Promise with the new attachment.
import { createBlob } from 'rxdb';
const attachment = await myDocument.putAttachment(
{
id: 'cat.txt', // (string) name of the attachment
data: createBlob('meowmeow', 'text/plain'), // (Blob) data of the attachment
type: 'text/plain' // (string) type of the attachment-data like 'image/jpeg'
}
);Expo/React-Native does not support the Blob API natively. Make sure you use your own polyfill that properly supports blob.arrayBuffer() when using RxAttachments or use the putAttachmentBase64() and getDataBase64() so that you do not have to create blobs.
putAttachments()โ
Write multiple attachments to a RxDocument in a single atomic operation. This is more efficient than calling putAttachment() multiple times because it only performs one write to the storage. Returns a Promise with an array of the new attachments.
import { createBlob } from 'rxdb';
const attachments = await myDocument.putAttachments([
{
id: 'cat.txt',
data: createBlob('meowmeow', 'text/plain'),
type: 'text/plain'
},
{
id: 'dog.txt',
data: createBlob('woof', 'text/plain'),
type: 'text/plain'
}
]);putAttachmentBase64()โ
Same as putAttachment() but accepts a plain base64 string instead of a Blob.
const attachment = await doc.putAttachmentBase64({
id: 'cat.txt',
length: 4,
data: 'bWVvdw==',
type: 'text/plain'
});getAttachment()โ
Returns an RxAttachment by its id. Returns null when the attachment does not exist.
const attachment = myDocument.getAttachment('cat.jpg');allAttachments()โ
Returns an array of all attachments of the RxDocument.
const attachments = myDocument.allAttachments();allAttachments$โ
Gets an Observable which emits a stream of all attachments from the document. Re-emits each time an attachment gets added or removed from the RxDocument.
const all = [];
myDocument.allAttachments$.subscribe(
attachments => all = attachments
);RxAttachmentโ
The attachments of RxDB are represented by the type RxAttachment which has the following attributes/methods.
docโ
The RxDocument which the attachment is assigned to.
idโ
The id as string of the attachment.
typeโ
The type as string of the attachment.
lengthโ
The length of the data of the attachment as number.
digestโ
The hash of the attachments data as string.
The digest is NOT calculated by RxDB, instead it is calculated by the RxStorage. The only guarantee is that the digest will change when the attachments data changes.
revโ
The revision-number of the attachment as number.
remove()โ
Removes the attachment. Returns a Promise that resolves when done.
const attachment = myDocument.getAttachment('cat.jpg');
await attachment.remove();getData()โ
Returns a Promise which resolves the attachment's data as Blob. (async)
const attachment = myDocument.getAttachment('cat.jpg');
const blob = await attachment.getData(); // BlobgetDataBase64()โ
Returns a Promise which resolves the attachment's data as base64 string.
const attachment = myDocument.getAttachment('cat.jpg');
const base64Database = await attachment.getDataBase64(); // 'bWVvdw=='getStringData()โ
Returns a Promise which resolves the attachment's data as string.
const attachment = await myDocument.getAttachment('cat.jpg');
const data = await attachment.getStringData(); // 'meow'Inline attachments on insert and upsertโ
Instead of inserting a document first and then calling putAttachment() separately, you can include attachments directly in the document data when using insert(), bulkInsert(), upsert(), bulkUpsert(), or incrementalUpsert(). Provide _attachments as an array of { id, type, data } objects.
import { createBlob } from 'rxdb';
// insert with inline attachments
const doc = await myCollection.insert({
name: 'foo',
_attachments: [
{
id: 'photo.jpg',
type: 'image/jpeg',
data: myJpegBlob
},
{
id: 'notes.txt',
type: 'text/plain',
data: createBlob('some notes', 'text/plain')
}
]
});
const attachment = doc.getAttachment('photo.jpg');Upsert behavior with attachmentsโ
When upserting a document that already exists, attachments from the new data are merged with the document's existing attachments by default. This means existing attachments not mentioned in the upsert data are preserved.
To replace all existing attachments instead, pass { deleteExistingAttachments: true } as the second argument:
// Merge (default): keeps existing attachments, adds/updates new ones
const doc = await myCollection.upsert(docData);
// Replace: only the attachments in docData will exist after the upsert
const doc2 = await myCollection.upsert(docData, { deleteExistingAttachments: true });This option works with upsert(), bulkUpsert(), and incrementalUpsert().
Attachment compression
Storing many attachments can be a problem when the disc space of the device is exceeded.
Therefore it can make sense to compress the attachments before storing them in the RxStorage.
With the attachments-compression plugin you can compress the attachments data on write and decompress it on reads.
This happens internally and will not change how you use the API. The compression is run with the Compression Streams API which is only supported on newer browsers.
MIME-type-aware compressionโ
The compression plugin is MIME-type-aware. It only compresses attachment types that benefit from compression (text, JSON, SVG, etc.) and passes through already-compressed formats (JPEG, PNG, MP4, etc.) as-is. This avoids wasting CPU cycles on files that won't shrink.
A built-in default list of compressible types is used automatically. You can override it with the compressibleTypes option in your schema:
import {
wrappedAttachmentsCompressionStorage
} from 'rxdb/plugins/attachments-compression';
import { getRxStorageIndexedDB } from 'rxdb-premium/plugins/storage-indexeddb';
// create a wrapped storage with attachment-compression.
const storageWithAttachmentsCompression = wrappedAttachmentsCompressionStorage({
storage: getRxStorageIndexedDB()
});
const db = await createRxDatabase({
name: 'mydatabase',
storage: storageWithAttachmentsCompression
});
// set the compression mode at the schema level
const mySchema = {
version: 0,
type: 'object',
properties: {
// ..
},
attachments: {
compression: 'deflate', // <- Specify the compression mode here. OneOf ['deflate', 'gzip']
// Optional: override which MIME types get compressed.
// Supports wildcard prefix matching (e.g. 'text/*' matches 'text/plain', 'text/html', etc.).
// If omitted, a built-in default list of compressible types is used.
compressibleTypes: [
'text/*',
'application/json',
'application/xml',
'image/svg+xml'
// ... add your own patterns
]
}
};
/* ... create your collections as usual and store attachments in them. */
The default compressible types include the following MIME type patterns. Binary formats like image/jpeg, image/png, video/*, and audio/* are not in the default list and will be stored without re-compression.
text/*application/jsonapplication/xmlapplication/xhtml+xmlapplication/javascriptapplication/x-javascriptapplication/ecmascriptapplication/rss+xmlapplication/atom+xmlapplication/soap+xmlapplication/wasmapplication/x-yamlapplication/sqlapplication/graphqlapplication/ld+jsonapplication/manifest+jsonapplication/schema+jsonapplication/vnd.api+jsonimage/svg+xmlimage/bmpfont/ttffont/otfapplication/x-font-ttfapplication/x-font-otfapplication/pdfapplication/rtfapplication/x-shapplication/x-cshapplication/x-httpd-php