Reputation: 42490
I create a s3 presigned URL in typescript as below:
const params = {
Bucket: myBucketName,
Key: uuidv4(),
Expires: 3600,
};
s3.getSignedUrlPromise('putObject', params)
and I used below code in flutter to upload image to this url:
Option1:
var formData = FormData.fromMap({
"file":
await MultipartFile.fromFile(image.path, filename: "upload.png"),
});
var response = await Dio().put(url, data: formData);
Option2:
return Dio().put(
url,
data: image.openRead(),
options: Options(
contentType: "multiple/form-data",
headers: {
"Content-Length": image].lengthSync(),
},
),
onSendProgress: (int sentBytes, int totalBytes) {
double progressPercent = sentBytes / totalBytes * 100;
print("upload $progressPercent %");
},
)
in both cases I got 403
forbidden error response. How can I upload images to the presigned URL? Is there anything wrong in the generated side or flutter side?
I can upload the file with curl
command like below:
curl -XPOST -H "Content-Type: application/x-www-form-urlencoded" --data-binary "@/Users/Pictures/IMG_4634.jpg" $PRESIGNED_URL
so the url does work.
Upvotes: 5
Views: 9687
Reputation:
I have tried all the solutions in this thread but it's not working
finally i found this blog: https://icircuit.net/flutter-upload-file-to-aws-s3/2885
this is the solution that works great with Dio:
Future<Response> sendFile(String url, File file) async {
Dio dio = new Dio();
var len = await file.length();
var response = await dio.put(url,
data: file.openRead(),
options: Options(headers: {
Headers.contentLengthHeader: len,
} // set content-length
));
return response;
}
Upvotes: 7
Reputation: 106
I created this class to send an single 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;
}
}
You can change await file.readAsBytes()
to this file.readAsBytesSync()
, if you are using File
Upvotes: 5
Reputation: 2196
I was getting 403 doing the same thing with presigned s3 urls and had to use a POST
request which returned a 204 and successfully saved the file.
Using the http package I did the example like so:
import 'package:http/http.dart' as http;
var req = http.MultipartRequest('POST', Uri.parse(s3Url));
req.files.add(
http.MultipartFile.fromBytes(
'file',
file.readAsBytesSync(),
filename: filename,
)
);
var res = await req.send();
print('UPLOAD: ${res.statusCode}');
This returns a StreamedResponse
type so to get the body of the response you need to convert the response.
NOTE: I did have to add a lot of fields on the multipart request like AWSAccessKeyId
and x-amz-security-token
which I was returning from the s3 presigned URL response. I configured it very securely though so you may not need that but wanted to clarify.
Upvotes: 3
Reputation: 42490
After some debugging, I found that this code can be used to fix it:
http.put(entry.value, body: image.readAsBytesSync())
I don't need to specify any header or any fields. And http.post
is google enough to solve the issue.
Upvotes: 2
Reputation: 3044
If you can print the response from S3 somehow, that may reveal the error.
From the information available, I think the problem is that your are using a PUT request and not a POST request. Looking at the Dio documentation, it seems like you can use Dio().post
. You may also need to set Content-Type
like in your curl example.
If it still doesn't work, try adding -v
to your curl command to see all of the headers it sends. It would be useful to get the network request that Dio sends, to compare against.
Upvotes: 2