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
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)")
}
}
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)
}
I want t upload a csv file from my iOS App (written in Swift) to my amazon S3 bucket. To do this I'm using following code:
//Create
let fileName = "Export.csv"
let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
csvText = CreateCSVAccount()
do {
try csvText.write(to: path!, atomically: true, encoding: String.Encoding.utf32BigEndian)
//Prepare Upload
let uploadingFileURL = path
let uploadRequest = AWSS3TransferManagerUploadRequest()
let Bucketname = "mybucket/CSV"
uploadRequest?.bucket = Bucketname
uploadRequest?.key = "mycsvfile.csv"
uploadRequest?.body = uploadingFileURL!
//Upload File
transferManager.upload(uploadRequest!).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask<AnyObject>) -> Any? in
if let error = task.error as NSError? {
if error.domain == AWSS3TransferManagerErrorDomain, let code = AWSS3TransferManagerErrorType(rawValue: error.code) {
switch code {
case .cancelled, .paused:
break
default:
print("Error Contact uploading: \(String(describing: uploadRequest?.key)) Error: \(error)")
}
} else {
print("Error Contact uploading: \(String(describing: uploadRequest?.key)) Error: \(error)")
}
return nil
}
let uploadOutput = task.result
print("Upload complete for: \(String(describing: uploadRequest?.key))")
print("uploadOutput: \(String(describing: uploadOutput))")
return nil
})
} catch {
print("Failed to create file")
print("\(error)")
}
The problem is sometimes it works and sometimes I'm receiving the following error:
Message=You did not provide the number of bytes specified by the Content-Length HTTP header, NumberBytesExpected=412, Code=IncompleteBody, RequestId=075D1F5B0A377E89
Can somebody please help me?
Thank you very much in advance!
Add the contentLength header to your request.
That is:
uploadRequest?.contentLength = 1234
where 1234 is an NSNumber representing the number of bytes in body
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;
}
I am developing an app which needs to store images in Azure using Swift.
Is there any example that will be helpful ? If not can you give me a suggestion ?
Here is a simple example.
1- Start here: https://azure.microsoft.com/en-us/documentation/articles/storage-ios-how-to-use-blob-storage/
2- Get the SDK
3- Here is the code:
let account = AZSCloudStorageAccount(fromConnectionString:AZURE_STORAGE_CONNECTION_STRING) //I stored the property in my header file
let blobClient: AZSCloudBlobClient = account.getBlobClient()
let blobContainer: AZSCloudBlobContainer = blobClient.containerReferenceFromName("<yourContainerName>")
blobContainer.createContainerIfNotExistsWithAccessType(AZSContainerPublicAccessType.Container, requestOptions: nil, operationContext: nil) { (NSError, Bool) -> Void in
if ((NSError) != nil){
NSLog("Error in creating container.")
}
else {
let blob: AZSCloudBlockBlob = blobContainer.blockBlobReferenceFromName(<nameOfYourImage> as String) //If you want a random name, I used let imageName = CFUUIDCreateString(nil, CFUUIDCreate(nil))
let imageData = UIImagePNGRepresentation(<yourImageData>)
blob.uploadFromData(imageData!, completionHandler: {(NSError) -> Void in
NSLog("Ok, uploaded !")
})
}
}
Enjoy :)
You have to use their REST API, but they're working on an SDK right now.
There are a couple of examples of using their REST API on iOS. A cursory search brings up: Uploading to azure blob storage from SAS URL returns 404 status
There is also this example on Github - https://github.com/Ajayi13/BlobExample-Swift
In iOS 11 and Swift 4, you can do like this:
private let containerName = "<Your Name>"
private let connectionString = "<Your String>"
do {
let account = try AZSCloudStorageAccount(fromConnectionString: connectionString)
let blobClient = account?.getBlobClient()
let blobContainer = blobClient?.containerReference(fromName: containerName)
let currentDate = Date()
let fileName = String(currentDate.timeIntervalSinceReferenceDate)+".jpg"
let blob = blobContainer?.blockBlobReference(fromName: now)
blob?.upload(from: imageData, completionHandler: {(error)->Void in
print(now, "uploaded!") // imageData is the data you want to upload
})
} catch {
print(error)
}
This is just an example. Hope it helps.