Flutter Upload S3 File Directly Using Presigned Url Created on the APP
String generatePresignedUrl(
String accessKey,
String secretKey,
String region,
String bucket,
String objectKey,
int expiresIn,
) {
final endpoint = '$bucket.s3.$region.amazonaws.com';
final datetime = DateTime.now().toUtc();
final dateStamp = DateFormat('yyyyMMdd').format(datetime);
final amzDate = DateFormat("yyyyMMdd'T'HHmmss'Z'").format(datetime);
final credentialScope = '$dateStamp/$region/s3/aws4_request';
final canonicalQueryString = 'X-Amz-Algorithm=AWS4-HMAC-SHA256'
'&X-Amz-Credential=${Uri.encodeComponent('$accessKey/$credentialScope')}'
'&X-Amz-Date=$amzDate'
'&X-Amz-Expires=$expiresIn'
'&X-Amz-SignedHeaders=host';
final canonicalRequest = 'PUT\n'
'/$objectKey\n'
'$canonicalQueryString\n'
'host:$endpoint\n'
'\n'
'host\n'
'UNSIGNED-PAYLOAD';
final stringToSign = 'AWS4-HMAC-SHA256\n'
'$amzDate\n'
'$credentialScope\n'
'${sha256.convert(utf8.encode(canonicalRequest)).toString()}';
final signingKey = _getSignatureKey(secretKey, dateStamp, region, 's3');
final signature =
Hmac(sha256, signingKey).convert(utf8.encode(stringToSign)).toString();
final presignedUrl = 'https://$endpoint/$objectKey?$canonicalQueryString'
'&X-Amz-Signature=$signature';
return presignedUrl;
}
List<int> _getSignatureKey(
String key, String dateStamp, String regionName, String serviceName) {
final kDate = Hmac(sha256, utf8.encode('AWS4$key'))
.convert(utf8.encode(dateStamp))
.bytes;
final kRegion = Hmac(sha256, kDate).convert(utf8.encode(regionName)).bytes;
final kService =
Hmac(sha256, kRegion).convert(utf8.encode(serviceName)).bytes;
final kSigning =
Hmac(sha256, kService).convert(utf8.encode('aws4_request')).bytes;
return kSigning;
}
Just Call generatePresignedUrl with required parameter and you will get your presigned URL then you can use that URL to make an PUT request to s3 and UPLOAD Your Files .
Here objectKey is your path which will stored in s3- like == assets/images/
TO GET YOUR URL
String generateStaticS3Url(String region, String bucket, String objectKey) {
return 'https://$bucket.s3.$region.amazonaws.com/$objectKey';
}
How to upload file using Presigned URl
Future<void> uploadFileToS3(File file, String presignedUrl) async {
try {
Dio dio = Dio();
// Open the file as a stream
var stream = file.openRead();
var length = await file.length();
Response response = await dio.put(
presignedUrl,
data: stream,
options: Options(
headers: {
'Content-Type':
'application/octet-stream', // Content-Type for binary data
'Content-Length':
length, // Important for setting the correct file length
},
sendTimeout: 60000, // 60 seconds timeout
receiveTimeout: 60000,
),
);
if (response.statusCode == 200) {
print('File uploaded successfully.');
} else {
print('Failed to upload file. Status code: ${response.statusCode}');
}
} on DioError catch (e) {
if (e.type == DioErrorType.sendTimeout) {
print('Connection timed out.');
} else if (e.type == DioErrorType.receiveTimeout) {
print('Receive timeout.');
} else if (e.type == DioErrorType.response) {
print('Server error. Status code: ${e.response?.statusCode}');
} else {
print('Error uploading file: ${e.message}');
}
} catch (e) {
print('Unexpected error: $e');
}
}
Using FilePicker to upload files
Future<String?> openFilePicker() async {
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
// Call S3 upload function
File file = File(result.files.single.path!);
// Ensure the file path includes the extension
String objectKey = file.path.split('/').last;
// Ensure the object key includes the extension
final presignedUrl = generatePresignedUrl(
'accessKey',
'SecretKey',
'ap-south-1',
'bucketName',
'assets/$objectKey',
3600,
);
print('Presigned URL: $presignedUrl');
await uploadFileToS3(File(result.files.single.path!), presignedUrl);
return objectKey;
}
return null;
}