Swift - Aws S3 Upload Image from Photo Library and Download It

Swift - AWS S3 Upload Image from Photo Library and download it

After doing many research I've got this working,

import AWSS3
import AWSCore

Upload:

I assume you have implemented UIImagePickerControllerDelegate already.

Step 1:

  • Create variable for holding url:

    var imageURL = NSURL()
  • Create upload completion handler obj:

    var uploadCompletionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?

Step 2: Get Image URL from imagePickerController(_:didFinishPickingMediaWithInfo:):

  • Set value of imageURL in this delegate method:

    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]){

    //getting details of image
    let uploadFileURL = info[UIImagePickerControllerReferenceURL] as! NSURL

    let imageName = uploadFileURL.lastPathComponent
    let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! as String

    // getting local path
    let localPath = (documentDirectory as NSString).stringByAppendingPathComponent(imageName!)

    //getting actual image
    let image = info[UIImagePickerControllerOriginalImage] as! UIImage
    let data = UIImagePNGRepresentation(image)
    data!.writeToFile(localPath, atomically: true)

    let imageData = NSData(contentsOfFile: localPath)!
    imageURL = NSURL(fileURLWithPath: localPath)

    picker.dismissViewControllerAnimated(true, completion: nil)
    }

Step 3: Call this uploadImage method after imageURL set to Upload Image to your bucket:

func uploadImage(){

//defining bucket and upload file name
let S3BucketName: String = "bucketName"
let S3UploadKeyName: String = "public/testImage.jpg"

let expression = AWSS3TransferUtilityUploadExpression()
expression.uploadProgress = {(task: AWSS3TransferUtilityTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) in
dispatch_async(dispatch_get_main_queue(), {
let progress = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print("Progress is: \(progress)")
})
}

self.uploadCompletionHandler = { (task, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)");
}
else{
print("Sucess")
}
})
}

let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()

transferUtility.uploadFile(imageURL, bucket: S3BucketName, key: S3UploadKeyName, contentType: "image/jpeg", expression: expression, completionHander: uploadCompletionHandler).continueWithBlock { (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let exception = task.exception {
print("Exception: \(exception.description)")
}
if let _ = task.result {
print("Upload Starting!")
}

return nil;
}
}

Download:

func downloadImage(){

var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?

let S3BucketName: String = "bucketName"
let S3DownloadKeyName: String = "public/testImage.jpg"

let expression = AWSS3TransferUtilityDownloadExpression()
expression.downloadProgress = {(task: AWSS3TransferUtilityTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) in
dispatch_async(dispatch_get_main_queue(), {
let progress = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print("Progress is: \(progress)")
})
}

completionHandler = { (task, location, data, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)")
}
else{
//Set your image
var downloadedImage = UIImage(data: data!)
}
})
}

let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()

transferUtility.downloadToURL(nil, bucket: S3BucketName, key: S3DownloadKeyName, expression: expression, completionHander: completionHandler).continueWithBlock { (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let exception = task.exception {
print("Exception: \(exception.description)")
}
if let _ = task.result {
print("Download Starting!")
}
return nil;
}

}

Ref. for upload image

Ref. for download image

Many thanks to jzorz

Upload Image to Amazon S3 Using Swift3

Okay I found the answer, but I have a different problem now that I'll post in another question regarding showing upload progress.

The answer was my bucket was created in the incorrect region. I created my credentials in Oregon, which is Us-West-2, and I created the bucket in Northern California by accident the first time. This apparently created the error.

How to upload to S3 bucket taking a photo with the iOS Camera

Most of the answers are outdated and too complicated. I was struggling with the same problem and finally found a solution.

This works best for me and works on Swift 5.

First of all, let's update the function to upload images to AWS.

func uploadToS3(url: URL) {
let fileArr = url.path.components(separatedBy: "/") // Path will be too long, so you have to separate the elements by / and store in an array
let key = fileArr.last // We get the last element of the array which in our case will be the image (my-image.jpg)
let localImageUrl = url

let request = AWSS3TransferManagerUploadRequest()!
request.bucket = bucketName
request.key = key
request.body = localImageUrl
request.acl = .publicReadWrite

let transferManager = AWSS3TransferManager.default()
transferManager.upload(request).continueWith(executor: AWSExecutor.mainThread()) { (task) -> Any? in
if let error = task.error {
print(error)
}
if task.result != nil {
print("Uploaded \(key)")
let contentUrl = self.s3Url.appendingPathComponent(bucketName).appendingPathComponent(key!)
self.contentUrl = contentUrl
}
return nil
}
}

In this block of code:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
takenPhoto.contentMode = .scaleToFill
takenPhoto.image = pickedImage
print(takenPhoto.image = pickedImage)
// Add here:
let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent("my-image", isDirectory: false)
.appendingPathExtension("jpg") /* here we are naming the image 'my-image' and it will be 'jpg', if you want you can add a counter to increase the number each time you upload an image, and you make something like this: "my-image-\(counter)"*/

// Then write to disk
if let data = pickedImage.jpegData(compressionQuality: 0.8) {
do {
try data.write(to: url)
uploadToS3(url: url) //Call the updated function to store to AWS bucket
} catch {
print("Handle the error, i.e. disk can be full")
}
}
}
picker.dismiss(animated: true, completion: nil)
}

With this implementation, the image will be uploaded immediately to the server once you select the image from the library.

How to upload a UIImage to S3 with AWS iOS SDK v2

It seems that AWSiOSSDKv2 don't have support to upload images from memory at this moment :(

From a Github issue:

The decision to accept only file NSURLs was driven by the following
factors:

  1. Since v1, the pause / resume features require the input to be
    files. It's not possible to recover NSData and retry the transfer when
    the app is killed.
  2. The background transfer on iOS 7 and above only
    supports files. Currently, we don't support background transfer, but
    we are planning to support it in the future. We considered accepting
    an NSData and internally persisting the data to a temporary directory.
  3. We decided not to include this in 2.0 release because if the NSData is
    backed by a file, it doubles the disk usage for the data. Also,
    developers have to deal with disk related errors when using
    S3TransferManager. Even though we decided not to accept NSData in 2.0
    release, we are open for your feedback. If this is a feature you want
    to see in the future release, please create a new issue with the
    feature request.

```

Upload image AWS S3 bucket in swift

I Have modified your code, try this

 let ext = "jpg"
let imageURL = NSBundle.mainBundle().URLForResource("imagename", withExtension: ext)
print("imageURL:\(imageURL)")

let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest.body = imageURL
uploadRequest.key = "\(NSProcessInfo.processInfo().globallyUniqueString).\(ext)"
uploadRequest.bucket = S3BucketName
uploadRequest.contentType = "image/\(ext)"

let transferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.upload(uploadRequest).continueWithBlock { (task) -> AnyObject! in
if let error = task.error {
print("Upload failed ❌ (\(error))")
}
if let exception = task.exception {
print("Upload failed ❌ (\(exception))")
}
if task.result != nil {
let s3URL = NSURL(string: "http://s3.amazonaws.com/\(self.S3BucketName)/\(uploadRequest.key!)")!
print("Uploaded to:\n\(s3URL)")
}
else {
print("Unexpected empty result.")
}
return nil
}

or you can use my code below to upload to AWS s3, its worked fine for me. This code is written in swift 3.

func uploadButtonPressed(_ sender: AnyObject) {
if documentImageView.image == nil {
// Do something to wake up user :)
} else {
let image = documentImageView.image!
let fileManager = FileManager.default
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("\(imageName!).jpeg")
let imageData = UIImageJPEGRepresentation(image, 0.99)
fileManager.createFile(atPath: path as String, contents: imageData, attributes: nil)

let fileUrl = NSURL(fileURLWithPath: path)
var uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = "BucketName"
uploadRequest?.key = "key.jpeg"
uploadRequest?.contentType = "image/jpeg"
uploadRequest?.body = fileUrl as URL!
uploadRequest?.serverSideEncryption = AWSS3ServerSideEncryption.awsKms
uploadRequest?.uploadProgress = { (bytesSent, totalBytesSent, totalBytesExpectedToSend) -> Void in
DispatchQueue.main.async(execute: {
self.amountUploaded = totalBytesSent // To show the updating data status in label.
self.fileSize = totalBytesExpectedToSend
})
}

let transferManager = AWSS3TransferManager.default()
transferManager?.upload(uploadRequest).continue(with: AWSExecutor.mainThread(), withSuccessBlock: { (taskk: AWSTask) -> Any? in
if taskk.error != nil {
// Error.
} else {
// Do something with your result.
}
return nil
})
}
}

Thanks :)

Show Upload Progress for Image Upload to Amazon S3 using Swift 3 and Amazon SDK

Okay I found the solution to my problem, apparently I just didn't follow through enough.

Here is my new revised uploadImage function

func uploadImage(filename:String){
print("AWS Upload Image Attempt...")
//defining bucket and upload file name
let S3BucketName: String = "distribution-tech-mobile-live"
let filepath = "\(AppDelegate.appDelegate.applicationDocumentsDirectory())/\(filename)"
let imageURL = URL(fileURLWithPath: filepath)
let S3UploadKeyName = filename //TODO: Change this later

let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = S3BucketName
uploadRequest?.key = S3UploadKeyName
uploadRequest?.contentType = "image/jpeg"
uploadRequest?.body = imageURL
uploadRequest?.serverSideEncryption = AWSS3ServerSideEncryption.awsKms
uploadRequest?.uploadProgress = { (bytesSent, totalBytesSent, totalBytesExpectedToSend) -> Void in
DispatchQueue.main.async(execute: {
self.amountUploaded = totalBytesSent // To show the updating data status in label.
self.fileSize = totalBytesExpectedToSend
print("\(totalBytesSent)/\(totalBytesExpectedToSend)")
})
}

let transferManager = AWSS3TransferManager.default()
transferManager?.upload(uploadRequest).continue(with: AWSExecutor.mainThread(), withSuccessBlock: { (taskk: AWSTask) -> Any? in
if taskk.error != nil {
// Error.
print("error")
} else {
// Do something with your result.
print("something with result when its done")
}
return nil
})

}

This has spot for when result is done and during the upload progress and makes a lot more sense.

Efficient way to upload multiple images to S3 from iOS

As I said on H. Al-Amri's response, if you need to know when the last upload is complete you can't simply iterate through an array of data and upload them all at once.

In Javascript there is a library (Async.js I think) that will make it easy to do background operations on individual elements of an array, and to get callbacks when each one finishes and when the entire array is finished. Since I'm not aware of anything like that for Swift, your intuition that you have to chain the uploads is correct.

As @DavidTamrazov explained in comments, you can chain your calls together using recursion. The way I solved this problem was a little more complicated since my network operations are done using NSOperationQueue to chain NSOperations. I pass an array of images to a custom NSOperation that uploads the first image from the array. When it completes, it adds another NSOperation to my NSOperationsQueue with the array of remaining images. When the array runs out of images, I know I'm complete.

Following is an example I chopped out of a much much larger chunk I use. Treat it as pseudocode because I edited it heavily and didn't have time to even compile it. But hopefully it's clear enough on the framework for how to do this with NSOperations.

class NetworkOp : Operation {
var isRunning = false

override var isAsynchronous: Bool {
get {
return true
}
}

override var isConcurrent: Bool {
get {
return true
}
}

override var isExecuting: Bool {
get {
return isRunning
}
}

override var isFinished: Bool {
get {
return !isRunning
}
}

override func start() {
if self.checkCancel() {
return
}
self.willChangeValue(forKey: "isExecuting")
self.isRunning = true
self.didChangeValue(forKey: "isExecuting")
main()
}

func complete() {
self.willChangeValue(forKey: "isFinished")
self.willChangeValue(forKey: "isExecuting")
self.isRunning = false
self.didChangeValue(forKey: "isFinished")
self.didChangeValue(forKey: "isExecuting")
print( "Completed net op: \(self.className)")
}

// Always resubmit if we get canceled before completion
func checkCancel() -> Bool {
if self.isCancelled {
self.retry()
self.complete()
}
return self.isCancelled
}

func retry() {
// Create a new NetworkOp to match and resubmit since we can't reuse existing.
}

func success() {
// Success means reset delay
NetOpsQueueMgr.shared.resetRetryIncrement()
}
}

class ImagesUploadOp : NetworkOp {
var imageList : [PhotoFileListMap]

init(imageList : [UIImage]) {
self.imageList = imageList
}

override func main() {
print( "Photos upload starting")
if self.checkCancel() {
return
}

// Pop image off front of array
let image = imageList.remove(at: 0)

// Now call function that uses AWS to upload image, mine does save to file first, then passes
// an error message on completion if it failed, nil if it succceeded
ServerMgr.shared.uploadImage(image: image, completion: { errorMessage ) in
if let error = errorMessage {
print("Failed to upload file - " + error)
self.retry()
} else {
print("Uploaded file")
if !self.isCancelled {
if self.imageList.count == 0 {
// All images done, here you could call a final completion handler or somthing.
} else {
// More images left to do, let's put another Operation on the barbie:)
NetOpsQueueMgr.shared.submitOp(netOp: ImagesUploadOp(imageList: self.imageList))
}
}
}
self.complete()
})
}

override func retry() {
NetOpsQueueMgr.shared.retryOpWithDelay(op: ImagesUploadOp(form: self.form, imageList: self.imageList))
}
}

// MARK: NetOpsQueueMgr -------------------------------------------------------------------------------

class NetOpsQueueMgr {
static let shared = NetOpsQueueMgr()

lazy var opsQueue :OperationQueue = {
var queue = OperationQueue()
queue.name = "myQueName"
queue.maxConcurrentOperationCount = 1
return queue
}()

func submitOp(netOp : NetworkOp) {
opsQueue.addOperation(netOp)
}

func uploadImages(imageList : [UIImage]) {
let imagesOp = ImagesUploadOp(form: form, imageList: imageList)
self.submitOp(netOp: imagesOp)
}
}

Access Denied when trying to download a picture from the aws s3 bucket

Some operations are performed on the bucket, while other operations are performed at the object level.

Replace this line:

"Resource": "arn:aws:s3:::schooled-deployments-mobilehub-969166170",

with this:

"Resource": [
"arn:aws:s3:::schooled-deployments-mobilehub-969166170",
"arn:aws:s3:::schooled-deployments-mobilehub-969166170/*"
]

That will permit access to the objects.



Related Topics



Leave a reply



Submit