Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No destinationBucket support in storage copy? #1034

Open
RockStone opened this issue Sep 15, 2024 · 1 comment
Open

No destinationBucket support in storage copy? #1034

RockStone opened this issue Sep 15, 2024 · 1 comment
Assignees
Labels
enhancement New feature or request

Comments

@RockStone
Copy link

Is your feature request related to a problem? Please describe.

Yes, the current storage_client package (version 2.0.3) does not support copying files between different buckets. While the supabase-js library includes an option to specify a destinationBucket when copying a file, the Flutter version only allows copying files within the same bucket. This inconsistency between the platforms leads to frustration when working with Supabase storage in Flutter, especially when there is a need to move files across buckets. It becomes inconvenient to download the file from one bucket and re-upload it to another, which introduces unnecessary complexity and performance issues.

For example, the Flutter SDK code in storage_client-2.0.3/lib/src/storage_file_api.dart currently looks like this:

Future<String> copy(String fromPath, String toPath) async {
  final options = FetchOptions(headers: headers);
  final response = await _storageFetch.post(
    '$url/object/copy',
    {
      'bucketId': bucketId,
      'sourceKey': fromPath,
      'destinationKey': toPath,
    },
    options: options,
  );
  return (response as Map<String, dynamic>)['Key'] as String;
}

Whereas supabase-js supports copying across buckets, as seen here:

async copy(
  fromPath: string,
  toPath: string,
  options?: DestinationOptions
): Promise<
  | {
      data: { path: string }
      error: null
    }
  | {
      data: null
      error: StorageError
    }
> {
  const data = await post(
    this.fetch,
    `${this.url}/object/copy`,
    {
      bucketId: this.bucketId,
      sourceKey: fromPath,
      destinationKey: toPath,
      destinationBucket: options?.destinationBucket,
    },
    { headers: this.headers }
  );
  return { data: { path: data.Key }, error: null };
}

This inconsistency severely limits the functionality of the Flutter SDK when compared to other platforms, and it results in a fragmented experience across different languages (such as TypeScript and Swift) for developers working with Supabase storage.

Describe the solution you'd like

I would like the supabase-flutter package to support the destinationBucket parameter in the copy method, just as it is supported in supabase-js and supabase-swift. This would enable developers to copy files across different buckets within Supabase storage without the need for workarounds like downloading and re-uploading files.

The new copy method signature in the Flutter package should look like this:

Future<String> copy(String fromPath, String toPath, {String? destinationBucket}) async {
  final options = FetchOptions(headers: headers);
  final response = await _storageFetch.post(
    '$url/object/copy',
    {
      'bucketId': bucketId,
      'sourceKey': fromPath,
      'destinationKey': toPath,
      'destinationBucket': destinationBucket,
    },
    options: options,
  );
  return (response as Map<String, dynamic>)['Key'] as String;
}

This will provide a consistent and unified experience across different SDKs, and make file management more efficient and straightforward in Flutter.

Describe alternatives you've considered

The alternative I have considered involves manually downloading the file from the source bucket and then uploading it to the destination bucket, followed by potentially deleting the original file. While this workaround is technically possible, it is cumbersome and inefficient. It increases bandwidth usage, introduces additional code complexity, and results in poor performance for large files or when performing many file operations.

Here’s an example of the workaround:

Future<void> copyFileBetweenBuckets(String sourceBucket, String sourcePath, String destinationBucket, String destinationPath) async {
  try {
    // Download the file from the source bucket
    final response = await Supabase.instance.client.storage.from(sourceBucket).download(sourcePath);
    if (response == null) {
      throw Exception('Failed to download file from $sourceBucket/$sourcePath');
    }

    // Upload the file to the destination bucket
    await Supabase.instance.client.storage.from(destinationBucket).upload(destinationPath, response);

    debugPrint('File successfully copied to $destinationBucket/$destinationPath');
  } catch (e) {
    debugPrint('Error during file copy: $e');
  }
}

This alternative is not ideal as it is error-prone and involves additional steps that should be handled internally by the SDK.

Additional context

  • This feature is crucial for developers managing multiple buckets for different purposes, such as separating public and private content.
  • It is important to maintain parity between the various Supabase SDKs (JavaScript, Swift, Flutter, etc.) so developers can expect the same functionality across platforms.
  • The Swift implementation also supports the destinationBucket option, as shown here:
public func copy(
  from source: String,
  to destination: String,
  options: DestinationOptions? = nil
) async throws -> String {
  struct UploadResponse: Decodable {
    let Key: String
  }

  return try await execute(
    HTTPRequest(
      url: configuration.url.appendingPathComponent("object/copy"),
      method: .post,
      body: configuration.encoder.encode(
        [
          "bucketId": bucketId,
          "sourceKey": source,
          "destinationKey": destination,
          "destinationBucket": options?.destinationBucket,
        ]
      )
    )
  )
  .decoded(as: UploadResponse.self, decoder: configuration.decoder)
  .Key
}

It would greatly benefit the Flutter community to have this feature available as soon as possible to align with the functionality provided by the other SDKs.

@RockStone RockStone added the enhancement New feature or request label Sep 15, 2024
@Vinzent03 Vinzent03 self-assigned this Sep 15, 2024
@RockStone
Copy link
Author

@Vinzent03 thanks for taking care of this. will we have this on the next release? any ETA?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants