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

# Share media

Share media such as images, videos and files with users in a chat room.

Upload the media to your own storage service, such as AWS S3, and then use the `metadata` field to reference the location of the media when a user [sends a message](/docs/chat/rooms/messages#send). On the receiving end, display the media to [subscribers](/docs/chat/rooms/messages#subscribe) that received the message.

## Access control

Ensure that you add a layer of authentication server-side if the media shouldn't be publicly available.

Do not add signed URLs to the message `metadata` because everyone who receives the message will have access to it. Additionally, signed URLs typically have expiration dates and chat messages may be stored for longer.

If you serve the media directly from a service, such as AWS S3, consider saving a file name in the `metadata` of the message. Then use a mechanism for the user to request a signed URL to the file.

## Upload media

Users need to be able to choose which media to attach to their message in your app. Write an upload function and make it available in the chat app context.

You can use either a unique identifier for the media, or a URL to its location. Ensure that any identifier is unique and that URLs are validated when they are received.

The following is an example of an upload function:

```javascript theme={null}
async function uploadMedia() {
  // ask the user to choose their media asynchronously
  // upload the media to your storage service
  // return a unique identifier for the media

  // mock implementation:
  let mediaId = 'abcd123abcd';

  // Some media metadata, useful for displaying the media in the UI
  let title = 'A beautiful image';
  let width = 1024;
  let height = 768;

  // Return the object
  return { id: mediaId, title, width, height };
}
```

Use the `uploadMedia()` flow to save the resulting object. In your UI, the `mediaToAttach` array should be displayed so that users can see which media will be attached to their message. It also enables users to add or remove selected media.

```javascript theme={null}
let mediaToAttach = [];
async function onMediaAttach() {
  const mediaData = await uploadMedia();
  mediaToAttach.push(mediaData);
}
```

## Send a message

Once a user has attached their media to the message, use the `metadata` field of the message to store its reference. `metadata` is a key-value object that can also be used to associate additional information such as its title and dimensions.

```javascript theme={null}
async function send(text) {
  let metadata = {};
  if (mediaToAttach.length > 0) {
    metadata["media"] = mediaToAttach;
    mediaToAttach = [];
  }
  await room.messages.send({
    text: text,
    metadata: metadata
  });
}
```

<Warning>
  Message `metadata` is not validated by the server. Always treat it as untrusted user input.
</Warning>

## Display media to subscribers

When a message is received that contains media, you need to display it in your app.

Firstly, make sure that you validate the `metadata`. If you are using IDs then ensure they are in the correct format, or if using URLs then validate the schema, domain, path and query parameters are as expected.

Define a function to get the valid media from a message:

```javascript theme={null}
// assume IDs are 10-15 characters long and alphanumeric
const mediaIdRegex = /^[a-z0-9]{10,15}$/;

function getValidMedia(message) {
  if (message.metadata.media && message.metadata.media.length > 0) {
    return message.metadata.media.filter(mediaData => mediaIdRegex.test(mediaData.id));
  }
  return [];
}
```

Use a function or component to display the message and its media:

```javascript theme={null}
function createMessageDOM(message) {
  const container = document.createElement("div");
  container.setAttribute('data-message-serial', message.serial)
  container.setAttribute('data-message-version', message.version.serial)

  const text = document.createElement("div");
  text.innerText = message.text;
  container.appendChild(text);
  const validMedia = getValidMedia(message);
  if (validMedia.length > 0) {
    const mediaContainer = document.createElement("div");
    for (let mediaData of validMedia) {
      const img = document.createElement("img");
      img.src = "https://example.com/images/"+mediaData.id;
      img.alt = mediaData.title;
      img.width = mediaData.width;
      img.height = mediaData.height;
      mediaContainer.appendChild(img);
    }
    container.appendChild(mediaContainer);
  }
  return container;
}
```

## Add media to an existing message

You can also add media to an existing message by editing its `metadata`:

```javascript theme={null}
let mediaId = 'abcd123abcd'; // assume this is the media we want to add
let message = ...; // assume this is the message we want to edit

const newMetadata = structuredClone(message.metadata);
if (!newMetadata.media) {
  newMetadata.media = [];
}
newMetadata.media.push(mediaId);

room.messages.update(message.serial, message.copy({metadata: newMetadata}))
```

## Remove media from an existing message

You can remove media from an existing message by updating its `metadata`:

```javascript theme={null}
let mediaId = 'abcd123abcd'; // assume this is the media we want to remove
let message = ...; // assume this is the message we want to edit

if (!message.metadata.media || message.metadata.media.length === 0) {
  //do nothing if there is no media
  return;
}
const newMetadata = structuredClone(message.metadata);
newMetadata.media = newMetadata.media.filter(id => mediaId !== id);

room.messages.update(message.serial, message.copy({metadata: newMetadata}))
```

## Media moderation

Ably [moderation feature](/docs/chat/moderation) is currently limited to text moderation. To add automatic or human moderation for media, you'll need to implement moderation server-side.

An example flow for this would be:

<Steps>
  <Step title="Upload media">
    Upload the media to your storage service.
  </Step>

  <Step title="Start moderation">
    Asynchronously start the moderation process on the server.
  </Step>

  <Step title="Send message">
    Send a message with the `metadata` containing information about the media; either an ID or URL.
  </Step>

  <Step title="Check moderation">
    When a user requests the media from your server, check the moderation status and serve it or return an error.
  </Step>
</Steps>

This means you don't need to update the `metadata` field of the message to reflect its moderation status.
