Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upload & fetch media files from AWS S3 in Flutter

My flutter app is using firebase as a backend but I need to store media files (photos & videos) in my s3 bucket. The mission is to upload the media retrieved from the image picker into s3 & get back the url, which can then be stored as a string in my firebase database.

The problem is a scarcity of aws libraries or api for dart 2. I found 3 in pub, but 2 of them were incompatible with dart 2 & 1 was under development. Has anyone implemented this in flutter using dart 2? Any suggestions are welcome. Thank you.

The packages I found were (pub.dartlang.org) : aws_client, aws_interop, amazon_s3

like image 803
Endemic Avatar asked Dec 17 '18 08:12

Endemic


People also ask

Is upload Cancelled?

The series will not return for a third season.

Will there be a 3rd season of upload?

It took some time, but Prime Video finally brought us good news during the middle of May. Upload is officially renewed for a third season. We'd already expected it based on the fact that writing had started before the official renewal came in, but it was good to get the confirmation.

What is to upload a file?

Uploading is the transmission of a file from one computer system to another, usually larger computer system. From a network user's point-of-view, to upload a file is to send it to another computer that is set up to receive it.

What happens to Dylan in upload?

Dylan died during a school trip to the Grand Canyon. More than likely there would not have been a way for him to upload to Lakeview before he actually died.


2 Answers

There's a few ways to do this. One way is to sign your request with Signature V4 and POST your file to S3.

First, create a policy helper:

import 'dart:convert';
import 'package:amazon_cognito_identity_dart/sig_v4.dart';

class Policy {
  String expiration;
  String region;
  String bucket;
  String key;
  String credential;
  String datetime;
  int maxFileSize;

  Policy(this.key, this.bucket, this.datetime, this.expiration, this.credential,
      this.maxFileSize,
      {this.region = 'us-east-1'});

  factory Policy.fromS3PresignedPost(
    String key,
    String bucket,
    String accessKeyId,
    int expiryMinutes,
    int maxFileSize, {
    String region,
  }) {
    final datetime = SigV4.generateDatetime();
    final expiration = (DateTime.now())
        .add(Duration(minutes: expiryMinutes))
        .toUtc()
        .toString()
        .split(' ')
        .join('T');
    final cred =
        '$accessKeyId/${SigV4.buildCredentialScope(datetime, region, 's3')}';
    final p = Policy(key, bucket, datetime, expiration, cred, maxFileSize,
        region: region);
    return p;
  }

  String encode() {
    final bytes = utf8.encode(toString());
    return base64.encode(bytes);
  }

  @override
  String toString() {
    return '''
{ "expiration": "${this.expiration}",
  "conditions": [
    {"bucket": "${this.bucket}"},
    ["starts-with", "\$key", "${this.key}"],
    {"acl": "public-read"},
    ["content-length-range", 1, ${this.maxFileSize}],
    {"x-amz-credential": "${this.credential}"},
    {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
    {"x-amz-date": "${this.datetime}" }
  ]
}
''';
  }
}

Then, sign your request with your policy helper and upload via http.MultipartRequest:

import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:async/async.dart';
import 'package:http/http.dart' as http;
import 'package:test/test.dart';
import 'package:amazon_cognito_identity_dart/sig_v4.dart';
import './policy.dart';

void main() {
  const _accessKeyId = 'AKXXXXXXXXXXXXXXXXXX';
  const _secretKeyId = 'xxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxx';
  const _region = 'ap-southeast-1';
  const _s3Endpoint =
      'https://bucketname.s3-ap-southeast-1.amazonaws.com';

  final file = File(path.join('/path/to/file', 'square-cinnamon.jpg'));
  final stream = http.ByteStream(DelegatingStream.typed(file.openRead()));
  final length = await file.length();

  final uri = Uri.parse(_s3Endpoint);
  final req = http.MultipartRequest("POST", uri);
  final multipartFile = http.MultipartFile('file', stream, length,
      filename: path.basename(file.path));

  final policy = Policy.fromS3PresignedPost('uploaded/square-cinnamon.jpg',
      'bucketname', _accessKeyId, 15, length,
      region: _region);
  final key =
      SigV4.calculateSigningKey(_secretKeyId, policy.datetime, _region, 's3');
  final signature = SigV4.calculateSignature(key, policy.encode());

  req.files.add(multipartFile);
  req.fields['key'] = policy.key;
  req.fields['acl'] = 'public-read';
  req.fields['X-Amz-Credential'] = policy.credential;
  req.fields['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
  req.fields['X-Amz-Date'] = policy.datetime;
  req.fields['Policy'] = policy.encode();
  req.fields['X-Amz-Signature'] = signature;

  try {
    final res = await req.send();
    await for (var value in res.stream.transform(utf8.decoder)) {
      print(value);
    }
  } catch (e) {
    print(e.toString());
  }
}

Please note that this method requires you to provide your Access Key and Secret Key. If you use a service like Cognito, it is recommended to get a temporary Access Key and Secret Key. Examples using temporary access found here.

Disclaimer: I am the original author of the Signature V4 package.

like image 156
Jon Saw Avatar answered Oct 21 '22 09:10

Jon Saw


You can use package amazon_s3_cognito to upload and delete the images to amazon s3.

I am the author of the plugin and we are using this plugin successfully in many of our projects.

        import 'package:amazon_s3_cognito/amazon_s3_cognito.dart';
        import 'package:amazon_s3_cognito/aws_region.dart';

        String uploadedImageUrl = await AmazonS3Cognito.uploadImage(
                  _image.path, BUCKET_NAME, IDENTITY_POOL_ID);


        //Use the below code to upload an image to amazon s3 server
        //I advise using this method for image upload.
        String uploadedImageUrl = await AmazonS3Cognito.upload(
                    _image.path,
                    BUCKET_NAME,
                    IDENTITY_POOL_ID,
                    IMAGE_NAME,
                    AwsRegion.US_EAST_1,
                    AwsRegion.AP_SOUTHEAST_1)

_image.path = path of the image you want to upload (file.path method in flutter)

IMAGE_NAME = this image uploads to s3 server with the name you give here.

        //use below code to delete an image
         String result = AmazonS3Cognito.delete(
                    BUCKET_NAME,
                    IDENTITY_POOL_ID,
                    IMAGE_NAME,
                    AwsRegion.US_EAST_1,
                    AwsRegion.AP_SOUTHEAST_1)

For fetching images you could use cached_network_image package
The CachedNetworkImage can be used directly or through the ImageProvider.

    CachedNetworkImage(
            imageUrl: "http://via.placeholder.com/350x150",
            placeholder: (context, url) => new CircularProgressIndicator(),
            errorWidget: (context, url, error) => new Icon(Icons.error),
         ),`enter code here`


    Future<String> _getFilePath(Asset asset,ListingImage listingImage) async{
       try {
         if (!isUploadCancelled) {
           // getting a directory path for saving
           final directory = await getTemporaryDirectory();
           String path = directory.path;
           File file = File(path + "/temp_" + listingImage.index.toString() + "_"+DateTime.now().microsecondsSinceEpoch.toString());
           listingImage.file = file;
           file = await file.writeAsBytes( asset.imageData.buffer.asUint8List(asset.imageData.offsetInBytes, asset.imageData.lengthInBytes));

           return file.path;
         } else {
           return null;
         }
       } catch(exceptioon) {
         return null;
       }

      }
like image 29
Prachi Shrivastava Avatar answered Oct 21 '22 09:10

Prachi Shrivastava