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:
- 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.- 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.- 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
Making Uitableview with Embedded Uicollectionview Using Uitableviewautomaticdimension
How to Access Extension of Uicolor in Swift
Understanding Model-View-Controller
How to Show Specific Language Keyboard When User Input Values in Uitextfield in iPhone App
Cannot Invoke 'Indexof' with an Argument List of Type '(Checklistitem)'
How We Can Set the Light Content Style of Status Bar in iOS 9 for Whole Application
iOS - Using Uisearchdisplaycontroller with Uisearchbar That Is Uibarbuttonitem in Uitoolbar
Apple Watchkit Simulator Issue: Sperrorinvalidbundlenogizmobinarymessage
Make Uisearchcontroller Search Bar Automatically Active
Private VS. Fileprivate on Declaring Global Variables/Consts in Swift3
Xcode 4.2 Mainstoryboard Not Found
Add a Navigation Bar to a View Without a Navigation Controller
Add Uiimage on Top of Another Uiimage
Facebook Sdk - iOS - Fail to Share Url (Error 102)
Uiwebview Stringbyevaluatingjavascriptfromstring Hangs on iOS5.0/5.1 When Called Using Gcd
iOS Network Reachability - Doesn't Seem to Be Working
Accessing Objective-C Base Class's Instance Variables from a Swift Class