Images and Video

Working with image and video blobs

Using blobs

"Blobs" are media files stored alongside an account's repository. They include images, video, and audio, but could also include any other file format. Blobs are referenced by individual records by the blob lexicon datatype, which includes a content hash (CID; ref) for the blob .

const imageBytes = fs.readFileSync('./photo.jpg')

const uploadRes = await client.call(com.atproto.repo.uploadBlob, {
  encoding: 'image/jpeg',
  data: imageBytes,
})

const blob = uploadRes.data.blob
console.log(JSON.stringify(blob, null, 2))
{
  "$type": "blob",
  "ref": {
    "$link": "bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku"
  },
  "mimeType": "image/jpeg",
  "size": 354028
}

Blob files are uploaded and distributed separately from records:

await client.create(app.bsky.feed.post, {
  text: 'Using a previously uploaded image',
  createdAt: new Date().toISOString(),
  embed: {
    $type: 'app.bsky.embed.images',
    images: [
      {
        alt: 'A photo uploaded earlier',
        image: blobRef,
        aspectRatio: { width: 1200, height: 800 },
      },
    ],
  },
})

You can see what an existing Blob record looks like in atproto.at. It's this one right here:

https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:ewvi7nxzyoun6zhxrhs64oiz/bafkreiebtvblnu4jwu66y57kakido7uhiigenznxdlh6r6wiswblv5m4py@jpeg

And here's what the URL to that beautiful blob looks like when served from a CDN:

https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:ewvi7nxzyoun6zhxrhs64oiz/bafkreiebtvblnu4jwu66y57kakido7uhiigenznxdlh6r6wiswblv5m4py

Note the URL structure: the blob is served from the CDN, but the path includes the DID of the account that uploaded it, as well as the CID of the blob itself.

Blob objects

Blobs are authoritatively stored by the account's PDS instance, but views are commonly served by CDNs associated with individual applications ("AppViews"), to reduce traffic on the PDS. CDNs may serve transformed (resized, transcoded, etc) versions of the original blob.

While blobs are universally content addressed (by CID), they are always referenced and managed in the context of an individual account (DID).

The empty blob (zero bytes) is valid in general, though it may be disallowed by individual Lexicons/applications.

Creating blobs requires the permission scope blob:*/* .

It is generally recommended to use an object storage implementation for working with blobs if you are hosting your own PDS. See going to production for more.

Further Reading and Resources