10

I am working on a Flutter App, where I upload image file (PUT Request) to AWS S3 using a presigned URL. The upload is successful as I can see the file in S3. But when I click and download it from the bucket, the downloaded file is corrupted.

I am using Dio library for uploading the file. Uploading the image file as binary via postman works perfectly

uploadFileToPresignedS3(
    File payload, String fileName, String presignedURL) async {
  try {
    Dio dio = new Dio();

    FormData formData = new FormData.from(
        {"name": fileName, "file1": new UploadFileInfo(payload, fileName)});
    dio.put(presignedURL, data: formData);
  } catch (ex) {
    print(ex);
  }
}


Expected: The uploaded file not to be corrupted

Actual result: The uploaded file is corrupted

FoxyError
  • 694
  • 1
  • 4
  • 19
Balaji
  • 101
  • 2
  • 5

3 Answers3

14

Use this code to upload file (image) to S3 pre-signed-url using Dio and show upload progress:

await dio.put(
    url,
    data: image.openRead(),
    options: Options(
      contentType: "image/jpeg",
      headers: {
        "Content-Length": image.lengthSync(),
      },
    ),
    onSendProgress: (int sentBytes, int totalBytes) {
      double progressPercent = sentBytes / totalBytes * 100;
      print("$progressPercent %");
    },
  );

Note: Do not set Content-Type header along with Content-Length like this:

headers: {
   "Content-Length": image.lengthSync(),
   "Content-Type": "image/jpeg",
},

Due to some reason, it will result in corrupted uploaded file.

Just in case: Instead of print("$progressPercent %") you can use setState() to show updates in UI.

Hope this helps.

Rabi Roshan
  • 1,268
  • 1
  • 11
  • 9
3

To piggy back off of Rabi Roshans's comment you need to modify contenType to "application/octet-stream". In your backend's S3 params you need to do the same.

client code

  await dio.put(
    url,
    data: image.openRead(),
    options: Options(
      contentType: "application/octet-stream",
      headers: {
        "Content-Length": image.lengthSync(),
      },
    ),
    onSendProgress: (int sentBytes, int totalBytes) {
      double progressPercent = sentBytes / totalBytes * 100;
      print("$progressPercent %");
    },
  );

s3 backend

  var presignedUrl = s3.getSignedUrl("putObject", {
    Bucket: "your_bucke_name",
    Key: "filename.ext", 
    Expires: 120, // expirations in seconds
    ContentType: "application/octet-stream", // this must be added or you will get 403 error
  })

;

Luis
  • 701
  • 8
  • 7
1

I created this class to send an image to s3 using pre-signed url, I'm using camera lib to send a photo to s3.

import 'dart:convert';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:http/http.dart';
import 'package:http_parser/http_parser.dart';

class AwsApi {
  Future<String> uploadToSignedUrl({required XFile file, required String signedUrl}) async {
    Uri uri = Uri.parse(signedUrl);
    var response = await put(uri, body: await file.readAsBytes(), headers: {"Content-Type": "image/jpg"});

    return response;

  }
}