I'm trying to upload an image to an S3 bucket using the putObject method in the AWS Swift SDK. I have followed the instructions here: https://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/examples-s3-objects.html
I am able to add strings to the s3 bucket, but can't figure out how to add images.
I have tried converting the images to jpegdata and pngdata to no avail.
Here is my code:
// AWS S3 image upload
func uploadFile(withImage image: UIImage) {
let s3Client = try? S3Client(region: "us-west-2")
let bucketName = "xxxxx"
let imageData = image.pngData()
guard let dataToUpload = "Text to upload working".data(using: .utf8) else {
return
}
let body = ByteStream.from(data: imageData!)
s3Client!.putObject(input: PutObjectInput(body: body, bucket: bucketName, contentType: "image/png", key: "Test")) { result in
switch(result) {
case .success(let response):
if let eTag = response.eTag {
print("Successfully uploaded the file with the etag: \(eTag)")
}
case .failure(let err):
print(err)
}
}
}
If I place "dataToUpload" into the ByteStream.from function, the data gets stored in the s3 bucket. If I use the imagedata, however, it does not.
Open to any and all solutions,
Thanks!!!
you can also Use AWSS3TransferUtility to upload files to S3
Steps
1)Setup bucket credential before uploading or simply in your app delegate by this method setupCreditial()
upload file to s3 by using the uploadFileToS3 method in your source view
/** regionType is your bucket region name identityPoolId is your bucket unique id*/
func setupCreditial(){
let credentialProvider = AWSCognitoCredentialsProvider(regionType: .AFSouth1, identityPoolId: "YourPoolID")
let config = AWSServiceConfiguration.init(region: .AFSouth1, credentialsProvider: credentialProvider)
AWSServiceManager.default().defaultServiceConfiguration = config
AWSS3TransferUtility.register(with: config!, forKey: "NAMEOFUTILITY")
}
/*
key is your aws3 key where you want to put image . in most cases it would public/key where key = any udid
content type is is file type like image,pdf,word etc
**/
public func uploadFileToS3(withKey key : String , contentType type : String, andData data : Data) {
let awsUploadExp = AWSS3TransferUtilityUploadExpression()
awsUploadExp.progressBlock = {task ,progress in
//progress goes here
}
guard let transferUtililty = AWSS3TransferUtility.s3TransferUtility(forKey: "NAMEOFUTILITY") else{
return
}
let completionHandler : AWSS3TransferUtilityUploadCompletionHandlerBlock = {(task,error)-> Void in
//handle your completion
}
transferUtililty.uploadData(data, bucket: "YourBucketname", key: key, contentType: type, expression: awsUploadExp, completionHandler: completionHandler)
}
Related
I've been attempting to upload images to an existing resource in S3, the images need to be publicly viewable and our website expects that the app sets the ACL to public-read on the file.
I have been unable to find a solution using the Amplify SDK that gets this done.
Currently even using the "guest" access level my images are not viewable at their S3 URLS.
Does anyone know how to set the "public-read" ACL during upload using the iOS Amplify SDK?
https://docs.amplify.aws/lib/storage/configureaccess/q/platform/ios/
Have you tried using "protected"?
I was able to hack together something that works for now using the escape hatch of the Amplify SDK.
https://docs.amplify.aws/lib/storage/escapehatch/q/platform/ios/
func uploadToS3(path: URL, data: Data, bucketName: String, uploadKeyName: String, contentType: String) {
do {
let plugin = try Amplify.Storage.getPlugin(for: "awsS3StoragePlugin") as? AWSS3StoragePlugin
if let escapedPlugin = plugin {
let awsS3 = escapedPlugin.getEscapeHatch()
let request = AWSS3PutObjectRequest()
if let req = request {
req.body = data
req.contentType = contentType
req.contentLength = NSNumber(integerLiteral: NSData(data: data).length)
req.bucket = bucketName
req.key = uploadKeyName
req.acl = .publicRead
awsS3.putObject(req).continueWith { (task) -> AnyObject? in
if let error = task.error {
print("there was an error with uploading image \(error)")
}
if task.result != nil {
let s3URL = NSURL(string: "http://s3.amazonaws.com/\(bucketName)/\(uploadKeyName)")
print("Uploaded to:\n\(s3URL)")
}
return nil
}
}
}
} catch {
print("Get escape hatch failed with error - \(error)")
}
}
We are trying to upload audio to AWS S3 bucket using method(AWSS3TransferUtility). Pod - AWSS3. We are getting below error.
Error description:
Error Domain=com.amazonaws.AWSS3TransferUtilityErrorDomain Code=2 "(null)" UserInfo={Server=AmazonS3, Transfer-Encoding=Identity, Connection=close, Content-Type=application/xml, Date=Fri, 06 Sep 2021 04:43:50 GMT, x-amz-request-id=VH53PCCWA529FDRC, x-amz-id-2=4uZoqJj+TJ93WUBSnrC889CAj3gkGGw/V6iJjhrVjB2+ZygTflGcPAV+amfxmeBGeGHHVXv3nHk=}
func uploadFile(withImage image: UIImage) {
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:AWSRegionType.USEast2,
identityPoolId:"us-east-2:fe41c293-df49-49a4-886d-094f2cd8d0fd")
let configuration = AWSServiceConfiguration(region:.USEast2, credentialsProvider:credentialsProvider)
let tuConf = AWSS3TransferUtilityConfiguration()
AWSS3TransferUtility.register(
with: configuration!,
transferUtilityConfiguration: tuConf,
forKey: "transfer-utility-with-advanced-options"
)
let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "transfer-utility-with-advanced-options")
AWSServiceManager.default().defaultServiceConfiguration = configuration
let s3BucketName = "testingimageupload"
let data: Data = image.pngData()!
let remoteName = generateRandomStringWithLength(length: 12)+"."+"png"
print("REMOTE NAME : ",remoteName)
let expression = AWSS3TransferUtilityUploadExpression()
expression.setValue("public-read", forRequestHeader: "x-amz-acl")
expression.progressBlock = { (task, progress) in
DispatchQueue.main.async(execute: {
// Update a progress bar
})
}
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
})
}
transferUtility!.uploadData(data, bucket: s3BucketName, key: "private/Abhay/" + remoteName, contentType: "image/"+"png", expression: expression, completionHandler: completionHandler).continueWith { (task) -> Any? in
if let error = task.error {
print("Error : \(error.localizedDescription)")
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(s3BucketName).appendingPathComponent(remoteName)
if let absoluteString = publicURL?.absoluteString {
// Set image with URL
print("Image URL : ",absoluteString)
}
}
return nil
}
Prerequisite : Setup AWS Amplify CLI.
1. Project initialisation.
amplify init: Setup AWS Amplify locally & remote.
Note: amplify init command not found
1)`npm install -g #aws-amplify/cli` - ### install the Amplify CLI ###
2)`curl -sL https://aws-amplify.github.io/amplify-cli/install | bash && $SHELL` ### configured $Path correctly ###
3)`amplify init` ###Try Amplify init###
Amplify Demo project or click enter for the default name : Enter a name for the project
Dev or click enter for the default name : Enter a name for the Environment
Choose Visual Studio Code : Choose your default Editor
iOS : Choose the type of app that you’re building
yes : Do you want to use an AWS Profile?
2. Project Storage setup
amplify add storage : Add storage
Content(Images, audio, video, etc.) : Select the below mentioned services
yes : (Amazon Cognito) Do you want to add auth now?
No: Do you want to configure advanced settings?
bucket name or click enter for the default name : Please provide bucket name
Auth users only or Auth and guest users : Who should have access
create/update , read , delete. : What kind of access do you want for Authenticated users?
create/update , read , delete.: What kind of access do you want for guest users?
No : Do you want add a lama trigger for your S3 Bucket?
3. Push - Amplify configuration to backend
amplify push : Amplify Push
Yes: are you sure you want to continue ?
Pod install
target ‘ProjectName do
use_frameworks!
pod 'Amplify'
pod 'AmplifyPlugins/AWSS3StoragePlugin'
pod 'AmplifyPlugins/AWSCognitoAuthPlugin'
end
Code: ViewController.swift
import Amplify
import AmplifyPlugins
func configureAmplify() {
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.add(plugin: AWSS3StoragePlugin())
try Amplify.configure()
print("Successfully configured Amplify")
} catch {
print("Could not configure Amplify", error)
}
}
func uploadFile() {
let audioUrl = URL(string: "/Users/appaiah/Downloads/RealAudio.mp3")!
Amplify.Storage.uploadFile(key: fileKey, local: audioUrl){ // let fileKey = "SampleAudio.mp3"
result in
switch result {
case .success(let key):
print("file with key uploaded \(key)")
case .failure(let error):
print("failed \(error)")
}
}
}
func downloadFile(){
let downloadToFileName = getTermsOfUseURL()
Amplify.Storage.downloadFile(
key: fileKey,
local: downloadToFileName,
progressListener: { progress in
print("Progress: \(progress)")
}, resultListener: { event in
switch event {
case .success:
print("Completed")
DispatchQueue.main.async { }
case .failure(let storageError):
print("Failed: \(storageError.errorDescription). \(storageError.recoverySuggestion)")
}
})
}
func getTermsOfUseURL() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
print(paths[0].appendingPathComponent(fileKey))
return paths[0].appendingPathComponent(fileKey)
}
In my app, I'm uploading the images attached by the user to S3 bucket in which server side encryption is used.
We have used the following code in Android to achieve this and it WORKED.
try
{
SSECustomerKey sseCustomerKey = new SSECustomerKey(BuildConfig.S3_AES_ENCRYPT_KEY);
CognitoCachingCredentialsProvider sCredProvider = new CognitoCachingCredentialsProvider(mContext, AWSCognitoPoolId, Regions.fromName(Regions.US_EAST_1.getName()));
AmazonS3Client sS3Client = new AmazonS3Client(sCredProvider);
PutObjectRequest putRequest = new PutObjectRequest(BuildConfig.S3_BUCKET_NAME, file.getName(), file).withSSECustomerKey(sseCustomerKey);
sS3Client.putObject(putRequest);
sS3Client.setRegion(Region.getRegion(Regions.fromName(Regions.US_EAST_1.getName())));
}
But in iOS, it is not working. Please find the following iOS code.
let transferManager = AWSS3TransferManager.default()
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = bucketName
uploadRequest?.body = fileURL
uploadRequest?.key = imageName[i]
uploadRequest?.serverSideEncryption = .AES256
uploadRequest?.sseCustomerKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
uploadRequest?.contentType = "image/jpeg"
transferManager.upload(uploadRequest!).continueWith(block: { (task) -> AnyObject? in
if let error = task.error as NSError? {
if error.domain == AWSS3TransferManagerErrorDomain as String {
if let errorCode = AWSS3TransferManagerErrorType(rawValue: error.code) {
switch (errorCode) {
case .cancelled, .paused:
DispatchQueue.main.async {
}
break;
default:
print("upload() failed: [\(error)]")
break;
}
} else {
print("upload() failed: [\(error)]")
}
} else {
print("upload() failed: [\(error)]")
}
}
return nil
})
I get the following error in iOS
upload() failed: [Error Domain=com.amazonaws.AWSS3ErrorDomain Code=0
"(null)" UserInfo={RequestId=C7302D0F4DD27397,
HostId=Dm3itGpwZNcpPq28qfFkKDlB2VFbOzIYn01T270QzzVXJ9lmZWU2bX7oPXyXrG5A86OpfTrXSHw=,
Message=Server Side Encryption with Customer provided key is
incompatible with the encryption method specified,
ArgumentValue=AES256, Code=InvalidArgument,
ArgumentName=x-amz-server-side-encryption}]
Please show me some light on this
I would recommend that you use TransferUtility instead of TransferManager. The TransferManager is on a deprecation path and doesn't have all the features that the TransferUtility has. Here is a code snippet showing how you can upload a file with server side encryption.
let transferUtility = AWSS3TransferUtility.default()
let uploadExpression = AWSS3TransferUtilityUploadExpression()
uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption")
uploadExpression.progressBlock = {(task, progress) in
print("Upload progress: ", progress.fractionCompleted)
}
let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
if let error = error {
//Error completing transfer. Handle Error
}
else {
//Successfully uploaded.
......
return nil
}
}
transferUtility.uploadData(
data,
bucket: "bucket",
key: "key",
contentType: "contenttype",
expression: uploadExpression,
completionHandler: uploadCompletionHandler
).continueWith (block: { (task) -> Any? in
if let error = task.error {
//Error initiating transfer. Handle error
}
return nil
})
}
Here is a link to more information on how to use TransferUtility - https://docs.aws.amazon.com/aws-mobile/latest/developerguide/how-to-transfer-files-with-transfer-utility.html
Now it's been years but still this answer would help someone and save hours of searching which i have gone through, TransferManager is deprecated so now we are using transferUtility s3 to upload files, But if we want to encrypt the files we have to send 3 keys in the header
expression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
expression.setValue(base64String, forRequestHeader: "x-amz-server-side-encryption-customer-key")
expression.setValue(md5String, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
These 3 keys are necessary otherwise the transfer utility won't upload the file and give u an error, x-amz-server-side-encryption-customer-algorithm this key is use to tell which encryption algorithm u want to use, For the other 2 keys we have to generate them, Sample code is this to generate base64String and md5, There are many ways to generate the key you can look at the CryptoSwift docs, I have used Salt which makes it more secure then brute force attacks
let input: Array<UInt8> = [0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9]
let password: [UInt8] = Array("s33krit".utf8)
let salt: [UInt8] = Array("nacllcan".utf8)
let iv: Array<UInt8> = AES.randomIV(AES.blockSize)
DispatchQueue.global().async {
do {
let key = try PKCS5.PBKDF2(password: password, salt: salt, iterations: 4096, keyLength: 32, variant: .sha2(.sha224)).calculate()
let encrypted = try AES(key: key, blockMode: CBC(iv: iv), padding: .pkcs7).encrypt(input)
let base64String: String = encrypted.toBase64()
let md5Data = encrypted.md5()
let md5DataBase64 = md5Data.toBase64()
print("Encrypted:\(encrypted),\n Base64String:\(base64String)")
print("md5:\(md5Data),\n md5String:\(md5DataBase64)")
completion(base64String,md5DataBase64)
} catch {
print(error)
}
}
You have to use CryptoSwift to generate these md5 and base64String keys to send in the headers, This will upload the encrypted file to the AWS and to open it or decrypt it you have to use the same base64 key
Hope this will help someone and save hours of time
I am uploading an image directly to AWS bucket which works perfectly fine from my location (India). The same code fails to upload an image when the user location is Singapore.
Following is the method I'm using to upload the image.
func uploadMediaOnS3(contentType: String = "application/octet-stream", mediaData: Data, folderName: String, fileName: String, progressBlock:#escaping (_ uploadProgress: Float, _ uploadStatus: Bool) -> Void) {
//Configure Credentials
let credentialProvider = AWSStaticCredentialsProvider(accessKey: "my-access-key", secretKey: "my-secret-key")
let configuration = AWSServiceConfiguration(region: .APSouth1, credentialsProvider: credentialProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
AWSServiceManager.default().defaultServiceConfiguration.timeoutIntervalForRequest = 90.0
AWSServiceManager.default().defaultServiceConfiguration.timeoutIntervalForResource = 90.0
//Setup Progress Block
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = {(task, progress) in
DispatchQueue.main.async {
print(" ==> \(Float(progress.fractionCompleted * 100))")
progressBlock(Float(progress.fractionCompleted * 100), false)
}
}
//Setup Completion Block
let uploadCompletionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock = { (task, error) -> Void in
DispatchQueue.main.async {
if error != nil {
progressBlock(-1.0, false)
} else {
progressBlock(100.0, true)
}
}
}
//Upload Data
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadData(mediaData, bucket: "my-bucket-name", key: "\(folderName)\(fileName)", contentType: contentType, expression: expression, completionHandler: uploadCompletionHandler).continueWith { (task) -> Any? in
if task.error != nil {
progressBlock(-1.0, false)
}
return nil
}
}
Following are my AWS SDK details :
AWSCore (version : 2.6.13)
AWSS3 (version : 2.6.13)
My guess would be that there is some issue with the region. I found out that my bucket location falls under .APSouth1 (Mumbai, India) and works fine here but when being used from Singapore, it fails due to the fact that it is falling under .APSoutheast1
One more thing to note, may be important, is that the upload fails instantly, as soon as it starts. There is no issue of timeout or anything.
It is also possible that I might be missing some settings here.
Thanks in advance!
func sendMultiPartAWS(path: String, imgData: Data, onComplete: #escaping (JSON, NSError?, URLResponse?) -> Void, onError: #escaping (NSError?, URLResponse?) -> Void) {
Alamofire.upload(imgData, to: path, method: .put).responseJSON { response in
if response.response?.statusCode == 200 {
let swiftyJSON = JSON.init(response.data!)
onComplete(swiftyJSON, nil, response.response)
} else {
}
}
}
I have used above code to upload image in AWS, may this help you.
Bucket name is global, but location is not. The following is quoted from Bucket Documentation:
Objects belonging to a bucket that you create in a specific AWS Region never leave that region, unless you explicitly transfer them to another region. For example, objects stored in the EU (Ireland) region never leave it.
You can use cross-region replication facility to have the same content across the regions
Im trying to upload image to Aws S3 bucket. I tried to follow a tutorial and I'm getting a error saying "Returning ENOTCONN because protocol has not yet been set up." I'm new to swift and I'm not able to understand why the error is occurring also.My code for S3 upload is as follows:
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.body = url!
uploadRequest?.key = remoteFileName
uploadRequest?.bucket = S3BucketName
uploadRequest?.contentType = "image/" + ext
let transferManager = AWSS3TransferManager.default()
// Perform Upload
transferManager.upload(uploadRequest!).continueWith(block: { (task:AWSTask<AnyObject>) -> AnyObject! in
if let error = task.error{
print("error \(error.localizedDescription)")
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent((uploadRequest?.bucket!)!).appendingPathComponent((uploadRequest?.key!)!)
print("Uploaded to:\(publicURL)")
}
return nil
})
My S3 is in ap-south-1 and cognito pool id in us-west-2. I guess thats creating the problem.Is there a way to fix the issue without creating another bucket in us-west-2.
I get the following error:
You want the bucket policy to be somewhat like this if the cognito pool is not set up for authentication: Notice Principal and Action values
Also, is there any particular reason you're using AWSS3TransferManagerUploadRequest? If the policy doesn't resolve your issue, you can use the following code for AWSS3TransferUtilityUploadExpression which sends your data in chunks asynchronously.
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = progressBlock
transferUtility.uploadData(UIImagePNGRepresentation(imageNew!)!,
bucket: "bucket-name",
key: (imgName.removeWhitespace()),
contentType: "image/png",
expression: expression,
completionHandler: completionHandler).continueWith { (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let _ = task.result {
print("Upload Starting!")
// Do something with uploadTask.
}
return nil;
}