Support attachments in your app built with XMTP
Use the remote attachment, multiple remote attachments, or attachment content type to support attachments in your app.
-
Use the remote attachment content type to send one remote attachment of any size.
-
Use the multiple remote attachments content type to send multiple remote attachments of any size.
-
Use the attachment content type to send attachments smaller than 1MB.
Support remote attachments of any size
One remote attachment of any size can be sent in a message using the RemoteAttachmentCodec
and a storage provider.
To send multiple remote attachments of any size in a single message, see Support multiple remote attachments of any size in a message.
Install the package
In some SDKs, the AttachmentCodec
is already included in the SDK. If not, you can install the package using the following command:
npm i @xmtp/content-type-remote-attachment
Configure the content type
After importing the package, you can register the codec.
import {
ContentTypeAttachment,
AttachmentCodec,
RemoteAttachmentCodec,
ContentTypeRemoteAttachment,
} from "@xmtp/content-type-remote-attachment";
// Create the XMTP client
const xmtp = await Client.create(signer, { env: "dev" });
xmtp.registerCodec(new AttachmentCodec());
xmtp.registerCodec(new RemoteAttachmentCodec());
Send a remote attachment
Load the file. This example uses a web browser to load the file:
//image is the uploaded event.target.files[0];
const data = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
if (reader.result instanceof ArrayBuffer) {
resolve(reader.result);
} else {
reject(new Error("Not an ArrayBuffer"));
}
};
reader.readAsArrayBuffer(image);
});
Create an attachment object:
// Local file details
const attachment = {
filename: image?.name,
mimeType: image?.type,
data: new Uint8Array(data),
};
Use RemoteAttachmentCodec.encodeEncrypted
to encrypt an attachment:
const encryptedEncoded = await RemoteAttachmentCodec.encodeEncrypted(
attachment,
new AttachmentCodec()
);
Upload an encrypted attachment to a location where it will be accessible via an HTTPS GET request. This location will depend on which storage provider you use based on your needs.
Now that you have a url
, you can create a RemoteAttachment
:
const remoteAttachment = {
url: url,
contentDigest: encryptedEncoded.digest,
salt: encryptedEncoded.salt,
nonce: encryptedEncoded.nonce,
secret: encryptedEncoded.secret,
scheme: "https://",
filename: attachment.filename,
contentLength: attachment.data.byteLength,
};
Now that you have a remote attachment, you can send it:
await conversation.send(remoteAttachment, {
contentType: ContentTypeRemoteAttachment,
});
Receive, decode, and decrypt a remote attachment
Now that you can send a remote attachment, you need a way to receive it. For example:
import { ContentTypeRemoteAttachment } from "@xmtp/content-type-remote-attachment";
if (message.contentType.sameAs(RemoteAttachmentContentType)) {
const attachment = await RemoteAttachmentCodec.load(message.content, client);
}
You now have the original attachment:
attachment.filename // => "screenshot.png"
attachment.mimeType // => "image/png",
attachment.data // => [the PNG data]
Once you've created the attachment object, you can create a preview to show in the message input field before sending:
const objectURL = URL.createObjectURL(
new Blob([Buffer.from(attachment.data)], {
type: attachment.mimeType,
})
);
const img = document.createElement("img");
img.src = objectURL;
img.title = attachment.filename;
To handle unsupported content types, refer to the fallback section.
Support multiple remote attachments of any size
Multiple remote attachments of any size can be sent in a single message using the MultiRemoteAttachmentCodec
and a storage provider.
Register necessary codecs
export const registerCodecs = () => {
Client.register(new AttachmentCodec());
Client.register(new RemoteAttachmentCodec());
Client.register(new MultiRemoteAttachmentCodec());
};
Create multiple attachment objects
Each attachment in the attachments array contains a URL that points to an encrypted EncodedContent
object. The content must be accessible by an HTTP GET
request to the URL.
const attachment1: DecryptedLocalAttachment = {
fileUri: "content://media/external/images/media/image-1.png",
mimeType: "image/png",
filename: "image-1.png"
}
const attachment2: DecryptedLocalAttachment = {
fileUri: "content://media/external/images/media/image-2.png",
mimeType: "image/png",
filename: "image-2.png"
}
Encrypt and upload multiple attachments to a remote server
const remoteAttachments: RemoteAttachmentInfo[] = []
for (const attachment of [attachment1, attachment2]) {
// Encrypt the attachment and receive the local URI of the encrypted file
const { encryptedLocalFileUri, metadata } = await alix.encryptAttachment(attachment)
// Upload the attachment to a remote server and receive the URL
// (Integrator must supply upload from local uri and return url functionality!)
const url = uploadAttachmentForUrl(encryptedLocalFileUri)
// Build the remote attachment info
const remoteAttachmentInfo =
MultiRemoteAttachmentCodec.buildMultiRemoteAttachmentInfo(url, metadata)
remoteAttachments.push(remoteAttachmentInfo)
}
Send a message with multiple remote attachments
await convo.send({
multiRemoteAttachment: {
attachments: remoteAttachments,
},
})
Recognize and decode a multi remote attachment
const messages = await conversation.messages()
if (messages.size > 0 && messages[0].contentTypeId == 'xmtp.org/multiRemoteStaticContent:1.0') {
// Decode the raw content back into a MultiRemoteAttachment
const multiRemoteAttachment: MultiRemoteAttachment = messages[0].content()
// See next section for download, and decrypt the attachments
}
Decode, download, and decrypt the attachments
const decryptedAttachments: DecryptedLocalAttachment[] = []
for (const attachment of multiRemoteAttachment.attachments) {
// Downloading the encrypted payload from the attachment URL and save the local file
// (Integrator must supply download from url and save to local Uri functionality!)
const encryptedFileLocalURIAfterDownload: string = downloadFromUrl(
attachment.url
)
// Decrypt the local file
const decryptedLocalAttachment = await alix.decryptAttachment({
encryptedLocalFileUri: encryptedFileLocalURIAfterDownload,
metadata: {
secret: attachment.secret,
salt: attachment.salt,
nonce: attachment.nonce,
contentDigest: attachment.contentDigest,
filename: attachment.filename,
} as RemoteAttachmentContent,
})
decryptedAttachments.push(decryptedLocalAttachment)
}
Accessing the unencrypted attachments
Use the file URIs in the decrypted attachments objects to display the attachments.
// Example showing displaying attachments if they represent images
const attachment1 = decryptedAttachments[0]
const attachment2 = decryptedAttachments[1]
<Image source={{ uri: attachment1.fileUri }} />
<Image source={{ uri: attachment2.fileUri }} />
Support attachments smaller than 1MB
Attachments smaller than 1MB can be sent using the AttachmentCodec
. The codec will automatically encrypt the attachment and upload it to the XMTP network.
Install the package
npm i @xmtp/content-type-remote-attachment
In some SDKs, the
AttachmentCodec
is already included in the SDK. If not, you can install the package using the following command:
Import and register
import {
ContentTypeAttachment,
AttachmentCodec,
} from "@xmtp/content-type-remote-attachment";
// Create the XMTP client
const xmtp = await Client.create(signer, { env: "dev" });
xmtp.registerCodec(new AttachmentCodec());
Load local file
// Read local file and extract its details
const file = fs.readFileSync("xmtp.png");
const filename = path.basename("xmtp.png");
const extname = path.extname("xmtp.png");
console.log(`Filename: ${filename}`);
console.log(`File Type: ${extname}`);
Send encrypted file
// Convert the file to a Uint8Array
const blob = new Blob([file], { type: extname });
let imgArray = new Uint8Array(await blob.arrayBuffer());
const attachment = {
filename: filename,
mimeType: extname, //image, video or audio
data: imgArray,
};
console.log("Attachment created", attachment);
await conversation.send(attachment, { contentType: ContentTypeAttachment });
Receive encrypted file
if (message.contentType.sameAs(ContentTypeAttachment)) {
const blobdecoded = new Blob([message.content.data], {
type: message.content.mimeType,
});
const url = URL.createObjectURL(blobdecoded);
}