Swift UIDocumentPickerViewController document upload into AWS S3 - ios

UIDocumentPickerViewController to pick some document and upload into AWS S3 with help of TransferUtility. Here, While uploading I need to show upload file name, status(progressive loader %),size into tableview cell. If i upload multiple files tableview cell need to show multiple cell with loading status.
Issues:
I have done UIDocumentPickerViewController to pick some documents and get those document URL,name,size,etc. but I don't know how to use (or) pass those values into upload part.
// MARK - File Storage Access
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
let fileurl: URL = url as URL
let filename = url.lastPathComponent
let fileextension = url.pathExtension
let filedata = url.dataRepresentation
print("DATA: \(filedata)","URL: \(fileurl)", "NAME: \(filename)", "EXTENSION: \(fileextension)")}
//Need to store above values and use it to below functions
Below upload part how to interact with tableview cell.
transferUtility.uploadData(data,bucket: S3BucketName,key: name,contentType: "text/plain",expression: expression,completionHandler: completionHandler).continueWith { (task) -> AnyObject? in
if let error = task.error {
print("Error: \(error.localizedDescription)")
DispatchQueue.main.async {
cells.statusLabel_util.text = "Failed"
print("Failed")
}
}
if let _ = task.result {
DispatchQueue.main.async {
print("Upload Starting!")
}
// Do something with uploadTask.
}
return nil;
}

You can upload file and track progress with this code
if let uploadRequest = AWSS3TransferManagerUploadRequest(){
uploadRequest.bucket = "your bucket"
uploadRequest.key = fileName
//uploadRequest.acl = AWSS3ObjectCannedACL.publicRead
uploadRequest.body = fileUrl
uploadRequest.uploadProgress = {(bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) -> Void in
DispatchQueue.main.async(execute: {() -> Void in
//Update progress
})
}
if let transferManager = AWSS3TransferManager.default(){
transferManager.upload(uploadRequest).continue(with: AWSExecutor.mainThread(), with: { (task) -> 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:
break
//print("Error uploading: \(uploadRequest?.key) Error: \(error)")
}
} else {
//print("Error uploading: \(uploadRequest?.key) Error: \(error)")
}
return nil
}
return nil
})
}
}

Related

FileProvider not load click file

I'm lost with the extension of File in iOS. I can show the files, navigate and show the thumbnails! However, when I click a file it does`t load, only show the text. Here is an image when I click an image:
error click
Here is my code that load the file when has clicked:
override func startProvidingItem(at url: URL, completionHandler: #escaping ((_ error: Error?) -> Void)) {
///TODO CHECK IF EXISTS THE FILE (WITH DATE OR ID)
//guard !fileManager.fileExists(atPath: url.path) else {
// completionHandler(nil)
// return
//}
guard
let identifier = persistentIdentifierForItem(at: url),
let file = FileProviderEnumerator.dictionary[identifier.rawValue]
else {
completionHandler(nil)
return
}
FilesManager.sharedInstance().downloadFile(withIdentifier: file.identifier) { progress in
} withCompletionHandler: { (urlFile, apiError) in
guard let urlFile = urlFile else {
completionHandler(FileProviderError.noContentFromServer)
return
}
do {
let data = try Data(contentsOf: urlFile, options: .alwaysMapped)
let created = self.fileManager.createFile(atPath: urlFile.path, contents:data, attributes:nil)
if(created) {
completionHandler(nil)
} else {
completionHandler(FileProviderError.noContentFromServer)
}
} catch let error{
print(error)
completionHandler(error)
}
/*
do{
let url = urlFile
itemCompletion(data, nil)
} catch let error{
print(error)
itemCompletion(nil, error)
}*/
//
}
}
Is it necessary to do something else? For example the method providePlaceHolder or something like that?
Really thanks in advance an any help with it.

delete file from s3 bucket swift

I have used this answer to delete a file from s3 bucket
The task I need to perform is when I'm uploading a new image I need to delete the previous one. So after upload, I'm calling deletion for the previous image. The deletion method gives me success but the file is still there in a bucket. So now, I can see both of the files; the previous one and the new one too. Am I missing something?
Here's my code of upload & delete
func uploadfile(image: UIImage, s3Key: String, bucketName: String, completionHandlerWithImageURL: #escaping(_ imageurl: String) -> ())
{
let transfer_utility = S3TransferUtilityConfig().config()
let expression = S3Expression().config()
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
debugPrint("Upload Completed")
}
let bucketName = bucketName
guard let data = image.pngData() else{
return
}
transfer_utility?.uploadData(data as Data, bucket: bucketName, key: s3Key, contentType: "image/png", expression: expression,
completionHandler: completionHandler).continueWith
{
(task) -> AnyObject? in
if let error = task.error {
print(error.localizedDescription)
completionHandlerWithImageURL("")
}
if let _ = task.result {
debugPrint("Uploaded image successfully")
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(bucketName).appendingPathComponent(s3Key)
if let absoluteString = publicURL?.absoluteString {
debugPrint("Image Url :\(absoluteString)")
completionHandlerWithImageURL(absoluteString)
}
} else {
debugPrint("Image URL not generated")
completionHandlerWithImageURL("")
}
}
return nil
}
}
func deleteExistingFile(s3Key: String, bucketName: String) {
let s3 = AWSS3.default()
guard let deleteObjectRequest = AWSS3DeleteObjectRequest() else { return }
deleteObjectRequest.bucket = bucketName
deleteObjectRequest.key = "/images/\(s3Key.components(separatedBy: "/").last ?? "")"
s3.deleteObject(deleteObjectRequest).continueWith { (task:AWSTask) -> AnyObject? in
if let error = task.error {
print("Error deleting file on s3 bucket: \(error)")
return nil
} else {
print("s3 file Deleted successfully. \(bucketName) \(s3Key)")
return nil
}
}
}
This is how I have called both the methods.
S3Manager.shared.uploadfile(image: image, s3Key: self.img_key, bucketName: (company_profile["dot"] as? String ?? ""))
{ (imgUrl) in
if imgUrl != ""
{
S3Manager.shared.deleteExistingFile(s3Key: self.profileUrl, bucketName: (company_profile["dot"] as? String ?? ""))
...
...
}
}
Uploaded image path:
https://s3.us-east-1.amazonaws.com/bucketNumber/images/img.png
I have tried deleteObjectRequest.key with the full path and only the folder / image path and in both the cases, method gives success but file is still there in a bucket.
deleteObjectRequest.key:
https://bucketName.s3.amazonaws.com/images/old_img.png
deleteObjectRequest.key:
/images/old_img.png

How to get value of completed task while uploading video to AWS?

I have implemented the AWSS3 to upload the video to the AWS server and also I want to get the value of task which has been completed using AWSS3TransferUtilityUploadExpression() to show the value on progress bar. But I am not getting the value please see the below code atached.
///1
typealias progressBlock = (_ progress: Double) -> Void //2
typealias completionBlock = (_ response: Any?, _ error: Error?) -> Void //3
//2
// Upload video from local path url
func uploadVideo(videoUrl: URL, progress: progressBlock?, completion: completionBlock?) {
print("video url is \(videoUrl)")
let fileName = self.getUniqueFileName(fileUrl: videoUrl)
print("keyname \(fileName)")
self.uploadfile(fileUrl: videoUrl, fileName: fileName, contenType: "video", progress: progress, completion: completion)
}
//method to upload the video
private func uploadfile(fileUrl: URL,
fileName: String,
contenType: String,
progress: progressBlock?, completion: completionBlock?) {
// Upload progress block
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = {(task, awsProgress) in
guard let uploadProgress = progress else { return }
DispatchQueue.main.async {
debugPrint("completed portion of the task is \(awsProgress.fractionCompleted)")
uploadProgress(awsProgress.fractionCompleted)
//progress!(awsProgress.fractionCompleted)
}
}
// Completion block
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if error == nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(self.bucketName).appendingPathComponent(fileName)
if let completionBlock = completion {
completionBlock(publicURL?.absoluteString, nil)
}
} else {
if let completionBlock = completion {
print("error is at completionBlock \(error?.localizedDescription)")
completionBlock(nil, error)
}
}
})
}
// Start uploading using AWSS3TransferUtility
let awsTransferUtility = AWSS3TransferUtility.default()
awsTransferUtility.uploadFile(fileUrl, bucket: bucketName, key: fileName, contentType: contenType, expression: expression, completionHandler: completionHandler).continueWith { (task) -> Any? in
if let error = task.error {
print("error is: \(error.localizedDescription)")
}
if let url = task.result {
// your uploadTask
print("url is \(url)")
}
return nil
}
}
Install cocoapods pod 'AWSS3'.
Here filePath is the path of the file to be uploaded.
func saveModelInAmazonS3() {
let remoteName = fileName + ".mov" //extension of your file name
let S3BucketName = "bucketName"
let uploadRequest = AWSS3TransferManagerUploadRequest()!
uploadRequest.body = filePath!
uploadRequest.key = remoteName
uploadRequest.bucket = S3BucketName
uploadRequest.contentType = "application/zip"
uploadRequest.acl = .publicRead
uploadRequest.uploadProgress = { (bytesSent, totalBytesSent, totalBytesExpectedToSend) -> Void in
DispatchQueue.main.async(execute: { // here you can track your progress
let amountUploaded = totalBytesSent
let fileSize = totalBytesExpectedToSend
print("\(amountUploaded)/\(fileSize)")
let progress = (CGFloat(amountUploaded) / CGFloat(fileSize)))
})
}
let transferManager = AWSS3TransferManager.default()
transferManager.upload(uploadRequest).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
self.delegate.errorInUpload(uploadState: self.uploadState)
print("Upload failed with error: (\(error.localizedDescription))")
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
print("Uploaded to:\(String(describing: url))")
}
return nil
})
}

How to save Amazon S3 files locally with Swift?

I've recently been working on a project that uploads and downloads files through the Amazon S3 service, specifically a user uploaded profile picture. I am also using Amazon Cognito (Username, full name, etc), and one of the things I like about Cognito is that it saves the downloaded data locally on the phone and displays it even when the app is offline. Then, when the app goes online, it updates any data changes. Unfortunately, the same doesn't happen with S3. Every time the app starts, it has to download the profile picture from my S3 bucket before displaying them. What I want to do is have the app save the user's profile picture from the S3 bucket on the iOS device so the user can see their picture even when the app goes offline. Here is what I currently have:
func downloadImage(){
var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?
//downloading image
let S3BucketName: String = "*******"
let S3DownloadKeyName: String = self.credentialsProvider.identityId + "/profileImage.png"
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)
// self.statusLabel.text = "Downloading..."
NSLog("Progress is: %f",progress)
})
}
completionHandler = { (task, location, data, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {
if ((error) != nil){
NSLog("Failed with error")
NSLog("Error: %#",error!);
//self.statusLabel.text = "Failed"
} else {
//self.statusLabel.text = "Success"
self.userProfile.image = UIImage(data: data!)
self.profileBackground.image = 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 {
NSLog("Error: %#",error.localizedDescription);
// self.statusLabel.text = "Failed"
}
if let exception = task.exception {
NSLog("Exception: %#",exception.description);
// self.statusLabel.text = "Failed"
}
if let _ = task.result {
// self.statusLabel.text = "Starting Download"
NSLog("Download Starting!")
// Do something with uploadTask.
}
return nil;
}
}
Thanks in advance!
This is a helper to download an image from S3 Bucket and save it on local phone
next time if you seek to this image. the helper will check if a local copy exists if not it will download from S3
import UIKit
import AWSCore
import AWSS3
class S3Helper: NSObject {
#objc var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?
#objc lazy var transferUtility = {
AWSS3TransferUtility.default()
}()
let DocDirPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
func downloadImage(imageView: UIImageView!, S3Folder: String, S3FileName: String) {
let S3DownloadKeyName: String = S3Folder + "/" + S3FileName
imageView.image = UIImage(named: "ic_place_holder.pdf")
let filePath = DocDirPath.appendingPathComponent(S3FileName).path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
let imageURL = self.DocDirPath.appendingPathComponent(S3FileName)
imageView.image = UIImage(contentsOfFile: imageURL.path)!
} else {
let expression = AWSS3TransferUtilityDownloadExpression()
expression.progressBlock = {(task, progress) in
DispatchQueue.main.async(execute: {
})
}
self.completionHandler = { (task, location, data, error) -> Void in
DispatchQueue.main.async(execute: {
if let error = error {
NSLog("Failed with error: \(error)")
//self.statusLabel.text = "Failed"
}
else{
//self.statusLabel.text = "Success"
imageView.image = UIImage(data: data!)
do{
let imgPath = URL(fileURLWithPath: self.DocDirPath.appendingPathComponent(S3FileName).path)
try (imageView.image ?? UIImage(named: "ic_place_holder.pdf")!).jpegData(compressionQuality: 1.0)?.write(to: imgPath, options: .atomic)
} catch let error1{
print(error1.localizedDescription)
}
}
})
}
transferUtility.downloadData(
forKey: S3DownloadKeyName,
expression: expression,
completionHandler: completionHandler).continueWith { (task) -> AnyObject? in
if let error = task.error {
NSLog("Error: %#",error.localizedDescription);
DispatchQueue.main.async(execute: {
//self.statusLabel.text = "Failed"
})
}
if let _ = task.result {
DispatchQueue.main.async(execute: {
//self.statusLabel.text = "Downloading..."
})
NSLog("Download Starting!")
// Do something with uploadTask.
}
return nil;
}
}
}
}

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

I've looked many amazon docs but didn't find enough information to upload and download images to S3 using Swift.
How can I do that?
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
If all you want is to download the image, this is a much more concise and correct way to do it:
func downloadImage(bucketName: String, fileName: String, completion: (image: UIImage?, error: NSError?) -> Void) {
let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()
transferUtility.downloadDataFromBucket(bucketName, key: fileName, expression: nil) { (task, url, data, error) in
var resultImage: UIImage?
if let data = data {
resultImage = UIImage(data: data)
}
completion(image: resultImage, error: error)
}
}
func uploadFile(with resource: String, type: String) {
let key = "\(resource).\(type)"
let localImagePath = Bundle.main.path(forResource: resource, ofType: type)
let localImageUrl = URL(fileURLWithPath: localImagePath!)
let transferManager1 = AWSS3TransferUtility.default()
let expression = AWSS3TransferUtilityUploadExpression()
self.uploadCompletionHandler = { (task, error) -> Void in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)");
}
else{
print("Sucess")
}
})
}
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadFile(localImageUrl, bucket: "", key: key, contentType: "video/mov", expression: expression, completionHandler: uploadCompletionHandler).continueWith { (task) -> AnyObject? in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let _ = task.result {
print("Upload Starting!")
}
return nil;
}
}
#IBAction func uplaodVideo(){
uploadFile(with: "random", type: "mov")
}
The above answers were really helpful to me, but they're quite outdated due to a lot of the nomenclature being changed. So I'm providing an updated version of the answer based on the latest Swift frameworks.
Image Picker Controller:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//getting details of image
let uploadFileURL = info[UIImagePickerController.InfoKey.referenceURL] as! NSURL
let imageName = uploadFileURL.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as String
// getting local path
let localPath = (documentDirectory as NSString).appendingPathComponent(imageName!)
//getting actual image
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let data = image.pngData()
let imageData = image.pngData()! as NSData
let photoURL = NSURL(fileURLWithPath: localPath)
self.uploadFileURL = photoURL
self.uploadImage(inputData: imageData)
do {
let result = try data?.write(to: photoURL as URL, options: .atomic)
} catch let error {
print(error)
}
picker.dismiss(animated: true, completion: nil)
}
Upload Image:
A quick note on upload image. I hashed my filenames. This is not a necessary step, but I highly recommend to do so in the production stage just to ensure no filenames conflict in your S3 bucket.
func uploadImage(inputData: NSData) {
//defining bucket and upload file name
let S3BucketName: String = "your_bucket_name"
let hashed = SHA256.hash(data: inputData)
let S3UploadKeyName: String = hashed.compactMap { String(format: "%02x", $0) }.joined()
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = {(task: AWSS3TransferUtilityTask, progress: Progress) in
print(progress.fractionCompleted)
}
self.uploadCompletionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)")
}
else{
print("Success")
}
})
}
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadFile(self.uploadFileURL! as URL, bucket: S3BucketName, key: S3UploadKeyName, contentType: "image/jpeg", expression: expression, completionHandler: uploadCompletionHandler).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
}
}

Resources