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
Related
I'm trying to get the UIImage from the local URL, but it doesn't work.
My someURl, where data is NSSecureCoding?: file:///Users/a19468455/Library/Developer/CoreSimulator/Devices/2FA5B725-9266-429C-A036-256D846BC3FA/data/Media/PhotoData/OutgoingTemp/3F73D172-AA12-40AD-A3AB-540543A43330/IMG_0004.JPG
Here's what I've tried (sacrificed code styling for convenience):
guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem else { return }
inputItems.forEach { item in
if let attachments = item.attachments,
!attachments.isEmpty {
attachments.forEach { attachment in
if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
var image: UIImage?
attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { [weak self] data, error in
guard let self = self else { return }
guard error == nil else {
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
return
}
if let someURl = data as? URL {
do {
let data = try Data(contentsOf: someURl)
image = UIImage(data: data)
} catch {
print("failed to retrieve data")
}
}
if let image = image {
images.append(image)
}
}
}
}
}
}
I also tried tips from here without success:
how to load image from local path ios swift (by path)
how to load image from local image path Ios Swift 4
Loading/Downloading image from URL on Swift
I have no way to get Data from my link, how do I fix it?
class FCMStorage {
var storage: Storage!
init() {
storage = Storage.storage()
}
func storeImage(data: Data?, name: String, completion: #escaping ((String?, Error?)->Void)) {
guard let data = data else {
return
}
let metaData = StorageMetadata()
metaData.contentType = "image/jpeg"
let path = "images/" + name
print("img to store = \(path)")
let ref = storage.reference()
let uploadTask = ref.child(path).putData(data, metadata: metaData) { (metadata, error) in
if error == nil {
print(metadata as Any)
ref.downloadURL(completion: { (url, error) in
completion(url?.absoluteString, error)
})
} else {
print("storeImage error: \(error!)")
}
}
uploadTask.observe(.progress) { (snapshot) in
print((snapshot.progress?.fractionCompleted ?? 0) * 100.0)
if snapshot.status == .success || snapshot.status == .failure {
uploadTask.removeAllObservers(for: .progress)
}
}
}
}
Using the able class I am able to upload the image on firebase successfully and I am able to see the uploaded image on the firebase too but...
When I call downloadURL() method it always giving me the following error
ref.downloadURL(completion: { (url, error) in
completion(url?.absoluteString, error)
})
Error: Failed to retrieve a download URL.
Anyone could help me out on this issue!!
EDIT
When I print metadata of the file it prints the following....
FIRStorageMetadata 0x283f99860: {
bucket = "sportoilic.appspot.com";
contentDisposition = "inline; filename*=utf-8''8E13A816-FAF1-47ED-8F84-94BBB8C4C77F";
contentEncoding = identity;
contentType = "application/octet-stream";
generation = 1601287237056536;
md5Hash = "VgMH6NMPGJT//LCD8goaDA==";
metageneration = 1;
name = "8E13A816-FAF1-47ED-8F84-94BBB8C4C77F";
size = 114787;
timeCreated = "2020-09-28T10:00:37.056Z";
updated = "2020-09-28T10:00:37.056Z";
}
and after that when I try to get the download url for the uploaded image< I am getting the above mentioned error(Error: Failed to retrieve a download URL).
What is the issue? Am I missing something here?
After trial and error of several hours, finally got the solution.
class FCMStorage {
var ref: StorageReference!
init(path: String) {
ref = Storage.storage().reference(withPath: path)
print("img to store at path = \(path)")
}
func storeImage(data: Data?, completion: #escaping ((String?, Error?)->Void)) {
guard let data = data else {
return
}
let uploadTask = ref.putData(data, metadata: nil) { (metadata, error) in
if error == nil {
print(metadata as Any)
self.ref.downloadURL(completion: { (url, error) in
completion(url?.absoluteString, error)
})
} else {
print("storeImage error: \(error!)")
}
}
uploadTask.observe(.progress) { (snapshot) in
print((snapshot.progress?.fractionCompleted ?? 0) * 100.0)
if snapshot.status == .success || snapshot.status == .failure {
uploadTask.removeAllObservers(for: .progress)
}
}
}
}
So store image just need to call like this...
let path = "images/" + UUID().uuidString
FCMStorage(path: path).storeImage(data: data) { (imgUrl, error) in
if let strUrl = imgUrl, error == nil {
FCMDatabase.init(OfTable: .chatRooms).setImageUrl(strUrl: strUrl, teamId: self.team?.id ?? 0, forMsgId: self.arrChats.last?.messageId ?? "") { (isDone) in
if isDone {
print("Image url set in database")
}
}
} else {
print("Error: \(error?.localizedDescription ?? "Error while getting image url")")
}
}
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
})
}
}
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;
}
}
}
}
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
}
}