## Upload File

To upload to S3 from a file, specify the `key` and the `localFile` to be uploaded, where the `localFile` can be an instance of `AWSFile` created from either an OS platform `File` instance or the result of Flutter file picker plugins such as [file_picker](https://pub.dev/packages/file_picker).

### Upload platform `File`

<Callout>

**Note**: To use `AWSFilePlatform`, add [aws_common](https://pub.dev/packages/aws_common) package to your Flutter project
by running: `flutter pub add aws_common`

</Callout>

<BlockSwitcher>

<Block name = "Mobile & Desktop">

```dart
import 'dart:io' as io;

import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'package:aws_common/vm.dart';

Future<void> uploadIOFile(io.File file) async {
  final awsFile = AWSFilePlatform.fromFile(file);
  try {
    final uploadResult = await Amplify.Storage.uploadFile(
      localFile: awsFile,
      key: 'upload/file.png',
    ).result;
    safePrint('Uploaded file: ${uploadResult.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading file: ${e.message}');
    rethrow;
  }
}
```

</Block>

<Block name = "Web">

```dart
import 'dart:html' as html;

import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'package:aws_common/web.dart';

Future<void> uploadHtmlFile(html.File file) async {
  final awsFile = AWSFilePlatform.fromFile(file);
  try {
    final uploadResult = await Amplify.Storage.uploadFile(
      localFile: awsFile,
      key: 'upload/file.png',
    ).result;
    safePrint('Uploaded file: ${uploadResult.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading file: ${e.message}');
    rethrow;
  }
}
```

</Block>

</BlockSwitcher>

### Upload with Flutter file_picker plugin

The [file_picker](https://pub.dev/packages/file_picker) plugin can be used to retrieve arbitrary file types from the user's device.

```dart
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:file_picker/file_picker.dart';

Future<void> uploadImage() async {
  // Select a file from the device
  final result = await FilePicker.platform.pickFiles(
    type: FileType.custom,
    withData: false,
    // Ensure to get file stream for better performance
    withReadStream: true,
    allowedExtensions: ['jpg', 'png', 'gif'],
  );

  if (result == null) {
    safePrint('No file selected');
    return;
  }

  // Upload file with its filename as the key
  final platformFile = result.files.single;
  try {
    final result = await Amplify.Storage.uploadFile(
      localFile: AWSFile.fromStream(
        platformFile.readStream!,
        size: platformFile.size,
      ),
      key: platformFile.name,
      onProgress: (progress) {
        safePrint('Fraction completed: ${progress.fractionCompleted}');
      },
    ).result;
    safePrint('Successfully uploaded file: ${result.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading file: $e');
    rethrow;
  }
}
```

## Upload Data

To upload to S3 from a data object, specify the `key` and `data`, where `data` is an instance of `S3DataPayload` created from various data formats.

<BlockSwitcher>

<Block name="String">

```dart
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';

Future<void> uploadStringData({
  required String dataString,
  required String key,
}) async {
  try {
    final result = await Amplify.Storage.uploadData(
      data: S3DataPayload.string(
        dataString,
        contentType: 'text/plain',
      ),
      key: key,
    ).result;

    safePrint('Uploaded data: ${result.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading data: ${e.message}');
    rethrow;
  }
}
```

</Block>

<Block name="JSON Object">

```dart
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';

Future<void> uploadJsonObject({
  required Map<String, Object?> json,
  required String key,
}) async {
  try {
    final result = await Amplify.Storage.uploadData(
      data: S3DataPayload.json(json),
      key: key,
    ).result;

    safePrint('Uploaded data: ${result.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading data: ${e.message}');
    rethrow;
  }
}
```

</Block>

<Block name="Data URL">

`S3DataPayload.dataUrl()` parses the provided data URL string, and throws exception if the data URL is invalid. See more info about [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs).

```dart
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';

Future<void> uploadDataUrl({
  required String dataUrl,
  required String key,
}) async {
  try {
    final result = await Amplify.Storage.uploadData(
      data: S3DataPayload.dataUrl(dataUrl),
      key: key,
    ).result;

    safePrint('Uploaded data: ${result.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading data: ${e.message}');
    rethrow;
  }
}
```

</Block>

<Block name="Bytes">

```dart
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';

Future<void> uploadBytes({
  required List<int> bytes,
  required String key,
  required String contentType,
}) async {
  try {
    final result = await Amplify.Storage.uploadData(
      data: S3DataPayload.bytes(
        bytes,
        contentType: contentType,
      ),
      key: key,
    ).result;

    safePrint('Uploaded data: ${result.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading data: ${e.message}');
    rethrow;
  }
}
```

</Block>

</BlockSwitcher>

## Upload Options

You can attach metadata while uploading data or a file by specifying the `metadata` property in options. If you want the `metadata` to be included in the upload result, you can set the `getProperties` flag to `true` in options.

```dart
Future<void> uploadWithOptions() async {
  // When uploading data, use `StorageUploadDataOptions`
  final uploadDataOperation = Amplify.Storage.uploadData(
    data: S3DataPayload.string(
      'example',
      contentType: 'text/plain',
    ),
    key: 'example.txt',
    options: const StorageUploadDataOptions(
      metadata: {
        'project': 'ExampleProject',
      },
      pluginOptions: S3UploadDataPluginOptions(
        getProperties: true,
      ),
    ),
  );
  final uploadDataResult = await uploadDataOperation.result;
  safePrint(
    'Uploaded data with metadata: ${uploadDataResult.uploadedItem.metadata}',
  );

  // When uploading a file, use `StorageUploadFileOptions`
  final uploadFileOperation = Amplify.Storage.uploadFile(
    localFile: AWSFile.fromPath('path/to/example.txt'),
    key: 'example.txt',
    options: const StorageUploadFileOptions(
      metadata: {
        'project': 'ExampleProject',
      },
      pluginOptions: S3UploadFilePluginOptions(
        getProperties: true,
      ),
    ),
  );
  final uploadFileResult = await uploadFileOperation.result;
  safePrint(
    'Uploaded file with metadata: ${uploadFileResult.uploadedItem.metadata}',
  );
}
```

The [`Amplify.Storage.getProperties` API](/lib/storage/get-properties) allows you to retrieve metadata without downloading the file.


In S3 console, you should see the metadata attached to your file as the following.

![S3 Metadata](/images/s3_metadata.png)


## Control of Upload Operations

A call to `Amplify.Storage.uploadFile` or `Amplify.Storage.uploadData` returns a reference to the operation that is performing the upload.

To cancel the upload (for example, in response to the user pressing a Cancel button), simply call `.cancel()` on the returned upload operation.

```dart
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';

S3UploadFileOperation? uploadOperation;

Future<void> uploadFile(String path) async {
  try {
    final storagePlugin = Amplify.Storage.getPlugin(AmplifyStorageS3.pluginKey);
    uploadOperation = storagePlugin.uploadFile(
      localFile: AWSFile.fromPath(path),
      key: 'example_file.txt',
    );

    final result = await uploadOperation!.result;
    safePrint('Uploaded ${result.uploadedItem.key}');
  } on StorageException catch (e) {
    safePrint('Error uploading file: ${e.message}');
  }
}

void cancelUpload() {
  uploadOperation?.cancel();
  uploadOperation = null;
}
```

## Multipart Upload

Amplify will automatically perform a S3 multipart upload for files larger than 5MB. For more information about S3's multipart upload support, see [Uploading and copying objects using multipart upload](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html).
