CometChat.createUploadFileRequest(receiverId, receiverType) returns an UploadFileRequest — the entry point for uploading files directly to storage with per-file progress, success, and failure. Upload is decoupled from sending: each uploaded file yields an Attachment (carrying a hosted url), which you then attach to a MediaMessage and send with sendMediaMessage().
A request object is scoped to one destination (receiverId / receiverType) and one upload batch. This is the recommended way to build a multi-attachment composer: create a request, upload a batch of files, show a progress bar per file, let the user remove or retry individual files, then send them as a single media message with multiple attachments (or split across several).
Why upload separately instead of passing files to
sendMediaMessage()?The classic path (passing a file straight to the MediaMessage constructor) uploads and sends in one blocking call — you get no progress, no per-file remove, and no per-file retry. An UploadFileRequest moves the upload out of the send call so you can drive a rich composer UI, then send instantly because the files are already hosted.The upload-then-send flow
1
Create a request
Call
CometChat.createUploadFileRequest(receiverId, receiverType). The recipient is required — the server uses it to apply role- and scope-based access control before issuing upload URLs.2
Upload
Call
request.uploadAttachments([{ fileId, file }], listener). Each file needs a caller-supplied fileId (required) that is echoed back on every event, so you can map callbacks to your UI rows. Validation, presigning, and the byte transfer run asynchronously and report through the listener.3
Track progress & handle failures
Your
UploadFileListener receives onFileProgress per file, then onFileUploaded (success), onFileError (rejected — not retryable), or onFileFailure (failed — retryable). Use request.removeAttachment(), or re-upload the same fileId to retry, as the user acts.4
Collect attachments
Each
onFileUploaded hands you an Attachment with a hosted url. You can also read them from the request at any time with request.getAttachments() / request.getAttachmentsByType().5
Build & send the message
Put the attachments on a
MediaMessage with setAttachments([...]) and call sendMediaMessage(). Because the attachments already have URLs, the message is sent as JSON — no re-upload.6
Clean up
Call
request.clearAll() after a successful send (or to abandon the composer) to release the batch from memory.Create an upload request
createUploadFileRequest() accepts:
Upload files
Build anUploadFileListener and call uploadAttachments() (or uploadAttachment() for a single file). Each item pairs a React Native file object with a required, caller-supplied fileId.
The file object is a React Native
FileInput:
fileId is caller-supplied and required. The SDK does not generate one — you provide a stable, unique id per file (echoed back unchanged on every event) so you can line each file up with its UI row. Re-uploading a fileId that is already in the batch overwrites its state and re-uploads it — this is exactly how you retry a failed file; use a fresh fileId for each genuinely new file.Validation, presigning, and the byte transfer all run asynchronously after uploadAttachments() returns — track outcomes through the listener.The UploadFileListener
Pass anUploadFileListener with only the callbacks you need — all are optional. Unlike MessageListener, it is not registered globally with a string id; it lives for the duration of the upload batch.
UploadResult
onComplete receives the batch’s settled state. It fires each time the batch drains — including after a retry adds new work and it drains again — and always reflects the whole batch, so keep the handler idempotent.
Configuring the request
Chainable setters let you configure the batch before (or between) uploads:Each request owns exactly one batch. For separate destinations (e.g. a main conversation and a thread), create a separate
UploadFileRequest for each — their file ids never cross.Adding more files to the batch
To add files incrementally (e.g. the user picks more while earlier uploads are still running), just calluploadAttachments() again on the same request — the new files join the same batch. Give each genuinely new file a fresh fileId; re-using an existing fileId re-uploads that file instead of adding a new one.
Per-call and global listeners
There are two listener scopes, and events fire on both:- Per-call listener — the
listeneryou pass touploadAttachment()/uploadAttachments(). It receives events only for the files in that call. - Global batch listener — registered with
request.addUploadListener(listener). It receives events for every file across all upload calls on the request. There is a single global slot: a lateraddUploadListener()overrides the previous one;removeUploadListener()clears it.
Reading the batch
Query the request’s current state at any time — useful when the user hits “send”:The attachment getters (
getAttachment, getAttachments, getAttachmentsByType) return only uploaded files, so they’re safe to hand straight to setAttachments(). getAttachmentCount() counts every file regardless of state, so you can compare it against getAttachments().length to see how many are still pending.Remove, retry & clear
All active upload batches are also cleared automatically on
CometChat.logout(). Since there is no send hook, call clearAll() yourself after a successful send so a batch doesn’t linger in memory until logout.Send the uploaded files as a media message
Once your files are uploaded, collect theirAttachments (from request.getAttachments() or from result.successful in onComplete), set them on a MediaMessage, and send. One batch can go out as a single multi-attachment message, or you can split the attachments across several messages — e.g. one message per media type using getAttachmentsByType().
sendMediaMessage() enforces the maximum attachments per message (see Limits). If a batch has more successful uploads than the limit allows, split them across multiple MediaMessages — grouping by kind with getAttachmentsByType() is a natural way to do this. See Multiple Attachments in a Media Message.Limits
Two independent checks guard the flow, each using a limit read from your app settings (with a built-in fallback):
Read the current attachment-count limit at runtime with
CometChat.getMaxAttachmentCount() — useful for disabling the “add file” button in your composer once the user hits the cap:
The per-file size limit is checked at upload time (before the byte transfer), while the attachment count limit is checked in
sendMediaMessage() (when you send). A batch can therefore upload successfully but still be rejected at send time if it exceeds the count limit — plan your composer around both.Reliability behavior
The SDK handles a few transport edge cases for you:- Stalled uploads — if a file makes no progress for 30 seconds, its upload is aborted and reported through
onFileFailurewithERR_UPLOAD_STALLED. Retry it by re-uploading the samefileId. - Expired upload URLs — the pre-signed upload URL for each file is valid for 15 minutes. The SDK re-presigns roughly 30 seconds before that TTL, and a retry requests a fresh URL if the original has expired — so retries keep working even after a long delay.
- Storage errors — if storage rejects the upload, the failure surfaces through
onFileFailurewithERR_S3_UPLOAD_FAILEDand, where available, the storage’s own message.
Error handling
Every error delivered toonFileError / onFileFailure (and rejections from sendMediaMessage()) is a CometChatException. Each non-successful file hits exactly one of onFileError (rejected — not retryable) or onFileFailure (failed — retryable). The upload-specific codes:
ERR_PERMISSION_DENIED and ERR_PRESIGN_REJECTED are returned by the server’s presign check, so their exact code and message come from your backend.Next Steps
Send A Message
Send text, media, and custom messages
Multiple Attachments
Send several attachments in one media message
Receive Messages
Listen for incoming messages in real-time
Edit a Message
Update an already-sent message