Activity Feeds V3 is in closed alpha — do not use it in production (just yet).

File Uploads

Stream allows you to easily upload files to our CDN and use them as attachments for activities.

This functionality defaults to using the Stream CDN. If you would like, you can easily change the logic to upload to your own CDN of choice.

How to upload a file or image

// Create an instance of AttachmentFile with the file path
//
// Note: On web, use `AttachmentFile.fromData`. Or if you are working with
// plugins which provide XFile then use `AttachmentFile.fromXFile`.
final file = AttachmentFile('path/to/file');

// Create a StreamAttachment with the file and type (image, video, file)
final streamAttachment = StreamAttachment(
  file: file,
  type: AttachmentType.image,
  custom: {'width': 600, 'height': 400},
);

// Upload the attachment
final result = await attachmentUploader.upload(
  streamAttachment,
  // Optionally track upload progress
  onProgress: (progress) {
    // Handle progress updates
  },
);

// Map the result to an Attachment model to send with an activity
final uploadedAttachment = result.getOrThrow();
final attachmentReadyToBeSent = Attachment(
  imageUrl: uploadedAttachment.remoteUrl,
  assetUrl: uploadedAttachment.remoteUrl,
  thumbUrl: uploadedAttachment.thumbnailUrl,
  custom: {...?uploadedAttachment.custom},
);

// Add an activity with the uploaded attachment
final activity = await feed.addActivity(
  request: FeedAddActivityRequest(
    attachments: uploadedAttachments,
    text: 'look at NYC',
    type: 'post',
  ),
);

// You can also automatically upload files as attachments when creating an activity
// by passing a list of payloads in the `attachmentUploads` field of the request

// Create a list of attachments to upload
final attachmentUploads = <StreamAttachment>[];

// Add an activity with the attachment needing upload
final activity = await feed.addActivity(
  request: FeedAddActivityRequest(
    attachmentUploads: attachmentUploads,
    text: 'look at NYC',
    type: 'post',
  ),
);

// It also works for comments!

// Create a list of attachments to upload
final attachmentUploads = <StreamAttachment>[];

// Add a comment with the attachment needing upload
final comment = await activity.addComment(
  request: ActivityAddCommentRequest(
    attachmentUploads: attachmentUploads,
    activityId: activity.activityId,
    comment: 'look at NYC',
  ),
);

Deleting Files and Images

We expose two methods for deleting files and images, client.deleteImage and client.deleteFile

// Delete an image from the CDN
await client.deleteImage(url: 'https://mycdn.com/image.png');

// Delete a file from the CDN
await client.deleteFile(url: 'https://mycdn.com/file.pdf');

Requirements for Images

  • Stream supported image types are: image/bmp, image/gif, image/jpeg, image/png, image/webp, image/heic, image/heic-sequence, image/heif, image/heif-sequence, image/svg+xml.

  • You can set a more restrictive list for your application if needed.

  • The maximum file size is 100MB.

Requirements for Files

  • Stream will not block any file types from uploading, however, different clients may handle different types differently or not at all.

  • You can set a more restrictive list for your application if needed.

  • The maximum file size is 100MB.

How to Allow/Block file extensions

Stream will allow any file extension. If you want to be more restrictive for an application, this is can be set via API or by logging into your dashboard.

  • To update via the dashboard, login and go to the Chat Overview page >> Upload Configuration.

  • API updates are made using UpdateAppSettings.

Image resizing

You can automatically resize an image appending query parameters to a valid image link stored on the Stream CDN.

An image can only be resized if the total pixel count of the source image is 16.800.000 or less. Attempting to resize an image with more pixels will result in an API error. An image of 4000 by 4000 would be accepted, but an image of 4100 by 4100 would pass the upper treshold for resizing.

There are four supported params - all of them are optional and can be used interchangeably:

ParameterTypeValuesDescription
wnumberWidth
hnumberHeight
resizestringclip, crop, scale, fillThe resizing mode
cropstringcenter, top, bottom, left, rightThe cropping direction during resize

Resized images will count against your stored files quota.

The Stream CDN URL returned during the upload contains a signature that validates the access to the file it points to. Only the members of a channel a file was uploaded to can see the attachment and its unique, signed link. Links can only be accessed with a valid signature, which also protects against enumeration attacks.

Whenever messages containing your attachments are retrieved (i.e., when querying a channel), the attachment links will contain a new, fresh signature.

A single Stream CDN URL expires after 14 days, after which its signature will stop working and the link won’t be valid anymore. You can check when a link will expire by comparing the current time with the Unix timestamp in the Expires parameter of the link’s query: https://us-east.stream-io-cdn.com/0000/images/foo.png?…&Expires=1602666347&…

Using Your Own CDN

All our SDKs make it easy to use your own CDN for uploads. The code examples below show how to change where files are uploaded:

// Your custom implementation of CdnClient
class CustomCDN implements CdnClient {
  @override
  Future<Result<UploadedFile>> uploadFile(
    AttachmentFile file, {
    ProgressCallback? onProgress,
    CancelToken? cancelToken,
  }) {
    // Use your own CDN upload logic here.
    // For example, you might upload the file to AWS S3, Google Cloud Storage, etc.
    // After uploading, return a Result<UploadedFile> with the file URLs.
    //
    // Note: Make sure to handle progress updates and cancellation if needed.
    return uploadToYourOwnCdn(
      file,
      onProgress: onProgress,
      cancelToken: cancelToken,
    );
  }

  @override
  Future<Result<UploadedFile>> uploadImage(
    AttachmentFile image, {
    ProgressCallback? onProgress,
    CancelToken? cancelToken,
  }) {
    // Use your own CDN upload logic here.
    // For example, you might upload the image to AWS S3, Google Cloud Storage, etc.
    // After uploading, return a Result<UploadedFile> with the image URLs.
    //
    // Note: Make sure to handle progress updates and cancellation if needed.
    return uploadToYourOwnCdn(
      image,
      onProgress: onProgress,
      cancelToken: cancelToken,
    );
  }

  @override
  Future<Result<void>> deleteFile(
    String url, {
    CancelToken? cancelToken,
  }) {
    // Use your own CDN deletion logic here.
    // For example, you might delete the file from AWS S3, Google Cloud Storage, etc.
    // After deleting, return a Result<void> indicating success or failure.
    //
    // Note: Make sure to handle cancellation if needed.
    return deleteFromYourOwnCdn(
      url,
      cancelToken: cancelToken,
    );
  }

  @override
  Future<Result<void>> deleteImage(
    String url, {
    CancelToken? cancelToken,
  }) {
    // Use your own CDN deletion logic here.
    // For example, you might delete the image from AWS S3, Google Cloud Storage, etc.
    // After deleting, return a Result<void> indicating success or failure.
    //
    // Note: Make sure to handle cancellation if needed.
    return deleteFromYourOwnCdn(
      url,
      cancelToken: cancelToken,
    );
  }
}

Future<void> usingYourOwnCdn() async {
  // Create a config with your custom CDN client
  final config = FeedsConfig(cdnClient: CustomCDN());

  // Initialize the StreamFeedsClient with the custom config
  final client = StreamFeedsClient(
    apiKey: 'your_api_key',
    user: const User(id: 'user_id'),
    config: config,
  );
}
© Getstream.io, Inc. All Rights Reserved.