How to upload images to Azure using Swift - ios

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.

Related

Set public-read ACL for AWS S3 data upload via iOS Amplify

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)")
}
}

Display Image downloaded from downloadURL generated by Firebase

I need to display the images that have been stored on the storage on Firebase. Right now, I only tracked the images using the link generated by function downloadURL:
func UploadImage(imageData: Data, path: String, completion: #escaping (String) -> ()){
let storage = Storage.storage().reference()
let uid = Auth.auth().currentUser?.uid
storage.child(path).child(uid ?? "").putData(imageData, metadata: nil) { (_, err) in
if err != nil{
completion("")
return
}
// Downloading Url And Sending Back...
storage.child(path).child(uid ?? "").downloadURL { (url, err) in
if err != nil{
completion("")
return
}
completion("\(url!)")
}
}
}
So all I can get is a hyperlink that is like: https://firebasestorage.googleapis.com/v0/b/getting-started-20f2f.appspot.com/o/profile_Photos%2FGQ1KR9H1mLZl2NAw9KQcRe7d72N2?alt=media&token=473ce86c-52ba-42ec-be71-32cc7dc895d7.
I refer to the official documentation, it seems that only when I have the name of the image file can I download it to an ImageView or UIImageView object. However, the link does not make any sense to me, so what can I do?
EDIT
I actually tried a solution provided by the official documentation:
func imageDownloader(_ imageURL: String) {
let store = Database.database().reference()
let uid = Auth.auth().currentUser?.uid
let imageRef = store.child(imageURL)
var myImageView = UIImageView()
imageRef.getData(completion: { (error, data) in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
}
})
}
But it suggests that I need to change something because Cannot convert value of type 'DataSnapshot' to expected argument type 'Data'.
If you're storing the image paths in Firestore, actually the exact file name does not matter if there is only one file available under the fork. So you just need to specify the path.
To then download the image from Storage, construct the path and download:
let uid = Auth.auth().currentUser?.uid
Storage.storage().reference().child("the\path\to\your\uid\collection").child(uid).getData(maxSize: 1048576, completion: { (data, error) in
if let data = data,
let img = UIImage(data: data) {
// do something with your image
} else {
if let error = error {
print(error)
}
// handle errors
}
})
You are uploading to Storage.storage(), but then in your imageDownloader, you're attempting to use Database.database(), which has a similar-looking API, but is, in fact, different.
Make sure to use Storage.storage() and that the closure parameters are in the order data, error in.
Finally, right now in your imageDownloader, it doesn't look like you're doing anything yet with var myImageView = UIImageView(), but keep in mind that you won't have access to the UIImage until the async getData completes.
Store your images at Firebase Storage & then retrieve using this code.
Storage.storage().reference.child("ProfilePhotos").child("ImageName").downloadURL {(url, _) in
DispatchQueue.main.async {
guard let url = url else { return }
imageView.setImage(with: url, placeholder: UIImage(named: "dummyImage"))
}
}

Firebase storage for image uploads

Issue with Firebase storage for image uploads: “Storage bucket cannot be initialised with a path”
Occurs when we try when pulling the putData method
We tried the following:
storage.storage().reference().child("Images").child("image1.jpg")
reference().document(object.id).setData(data,merge:true)
storage.storage().reference().child("Images").child("image1.jpg")
reference().document(object.id).setData(data,merge:true)
I understand that you have trouble storing the image, try this way
let imageName = "image1.jpg"
let image = UIImage(named: imageName)
let store = Storage.storage()
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let imageData: Data = UIImageJPEGRepresentation(image, 0.5)!
let storeRef = store.reference().child("Images")
let _ = storeRef.putData(imageData, metadata: metadata) { (metadata, error) in
guard let _ = metadata else {
print("error occurred: \(error.debugDescription)")
return
}
let pat = (metadata?.downloadURL()?.absoluteString.description)
let link = pat! //Link of image
}
Just ran into this Error message, took me a few hours because it crashes the app every time. Previously, I initialized my bucket for dev and prod using the full path, prefixed by gs://
Removed the gs:// and that fixed the problem:
Changed:
if (Config.of(context).isDev) {
FirebaseStorage.instance.bucket = "gs://dev-appname.appspot.com";
} else {
FirebaseStorage.instance.bucket = "gs://appname.appspot.com";
}
TO:
if (Config.of(context).isDev) {
FirebaseStorage.instance.bucket = "dev-appname.appspot.com";
} else {
FirebaseStorage.instance.bucket = "appname.appspot.com";
}

How to watch changement of firebase storage and update the viewcontroller on real time?

I'm building an app where a user can update his profile picture using firebase services (firebase storage).
In my app user can have multiple friends, and pictures of his friends is displayed in a view controller.
What I want to achieve is that when user updates his profile picture all his friends (connected using other devices) get his image changed in a real time.
For that I believe that the easiest solution is to watch the changes in the storage.
Here is the code I used to upload the user image
let storage = FIRStorage.storage()
let storageref = storage.reference(forURL: "my firebase url")
let imageref = storageref.child("images")
let userid : String = (user.uid)
let spaceref = imageref.child("\(userid).jpg")
let imageConverter = ImageConverter()
var profilePicture : UIImage = imageConverter.cropToBounds(image: self.profilePicture.image!, width: 150 ,height: 150)
profilePicture = imageConverter.resizeImage(image: profilePicture, targetSize: CGSize(width: 140, height: 150))
let imageData = UIImagePNGRepresentation(profilePicture)
_ = spaceref.put(imageData!, metadata: nil){ metadata, error in
if (error != nil) {
loadingScreenViewController.view.removeFromSuperview()
self.alert(title: "Error", description: "please check your internet connection and try again")
}
else {
_ = metadata!.downloadURL
}
loadingScreenViewController.view.removeFromSuperview()
Does anyone have an idea about how to watch changement in firebase storage.
I get the image simply by using
let imageRef = storage.child("images/\(friendsManager.friends[indexPath.row].id).jpg")
As far as I know Firebase doesn't support 'watching for changes' in the Storage module. The best way around this would be to store the URL to the profile images in the Realtime database, then listen for changes there and download the images accordingly. Use a randomly generated string for each image so you can ensure they're unique. Something like -
_ = spaceref.put(imageData!, metadata: nil){ metadata, error in
if (error != nil) {
loadingScreenViewController.view.removeFromSuperview()
self.alert(title: "Error", description: "please check your internet connection and try again")
} else {
if let downloadUrl = metadata.downloadURL() {
let db = Database.database().reference()
db.child("profiles").child("myuserid").setValue(["photoUrl" : downloadUrl])
}
}
Then watch for changes -
profilesRef.observe(.childChanged, with: { (snapshot) -> Void in
// Download the image from the stored url
})

uploading/downloading multiple images the right way?

i'm trying to upload or download images using Nuke(framework for downloading and Caching images) And Firebase to upload images as the backend
for single file it's easy to deal with without any problem
but for multiple ones i don't really know what to do right
i'm having an issues where it don't do it job synchronously
it downloads second image before the first one sometimes
i'll show my way of downloading and uploading multiple images
For download i put the code in for-loop
func downloadImages(completion: (result: [ImageSource]) -> Void){
var images = [ImageSource]()
for i in 0...imageURLs.count-1{
let request = ImageRequest(URL: NSURL(string:imageURLs[i])!)
Nuke.taskWith(request) { response in
if response.isSuccess{
let image = ImageSource(image: response.image!)
images.append(image)
if i == self.imageURLs.count-1 {
completion(result: images)
}
}
}.resume()
}
}
And for uploading where the user chooses the images
form image picker and return it as NSData array
And then perform this code
func uploadImages(completion: (result: [String]) -> Void){
let storageRef = storage.referenceForURL("gs://project-xxxxxxxxx.appspot.com/Uploads/\(ref.childByAutoId())")
var imageUrl = [String]()
var imgNum = 0
for i in 0...imageData.count-1 {
let imagesRef = storageRef.child("\(FIRAuth.auth()?.currentUser?.uid) \(imgNum)")
imgNum+=1
let uploadTask = imagesRef.putData(imageData[i], metadata: nil) { metadata, error in
if (error != nil) {
print("error")
imageUrl = [String]()
completion(result: imageUrl)
} else {
print("uploading")
// Metadata contains file metadata such as size, content-type, and download URL.
let downloadURL = metadata!.downloadURL()?.absoluteString
print(downloadURL)
imageUrl.append(downloadURL!)
if i == imageUrl.count-1{ //end of the loop
print("completionUpload")
completion(result: imageUrl)
}
}
}}
this is good way to do this task ?
what should i do to make each image downloads in order ?
please give me anything that may help example code , link etc ..
Thanks in advance
We highly recommend using Firebase Storage and the Firebase Realtime Database together to accomplish lists of downloads:
Shared:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
Upload:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
Download:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Now use Nuke (or another third party lib)
let request = ImageRequest(URL: NSURL(string:downloadURL)!)
Nuke.taskWith(request) { response in
// Do something with response
}
// Alternatively, you can use the Storage built-ins:
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Do something with data...
})
})
For more information, see Zero to App: Develop with Firebase, and it's associated source code, for a practical example of how to do this.

Resources