I have been able to successfully upload images to firebase storage but have been unable to successfully download the image.
I have attempted to download images in all three of the suggested ways on Firebase Storage Guides:
1) Download to NSData in memory
2) Download to an NSURL representing a file on device
3) Generate an NSURL representing the file online
An example is below of two different attempts:
func loadProfileImage() {
guard let currentUser = Auth.auth().currentUser else { return }
let profilePhotoFile = "ProfileImages/" + currentUser.uid
let reference = Storage.storage().reference(withPath: profilePhotoFile)
#1st Attempt downloading to memory:
reference.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if let error = error {
print("an error occurred: \(error)")
print("see data response: \(data)")
} else {
self.profileView.image = UIImage(data: data!)
}
}
#2nd Attempt with download url:
reference.downloadURL { (url, error) in
if let error = error {
print(error)
} else {
self.profileView.sd_setImage(with: url, placeholderImage:
#imageLiteral(resourceName: "placeHolderProfileView")) {
(image, error, type, reference2) in
print("reference location of image in the google
bucket: \(reference2)")
print("error retrieving image: \(String(describing:
error))")
print("type: \(type)")
print("image details: \(String(describing: image))")
}
}
}
}
Also tried using alamofire instead of SDWebImage to see if error code was same and it is the same see below:
Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={object=ProfileImages/6I2RhzFI3edYNph9J4WsaXXXX, ResponseErrorCode=100, bucket=bXXXX-production.appspot.com, NSLocalizedDescription=An unknown error occurred, please check the server response., ResponseErrorDomain=NSPOSIXErrorDomain, _kCFStreamErrorDomainKey=1, NSErrorPeerAddressKey={length = 28, capacity = 28, bytes = 0x1c1e01bb000000002607f8b040090813 ... 0000200a00000000}, _kCFStreamErrorCodeKey=100}
I have checked and rechecked the google storage bucket location and believe I have the reference location correct (using the same as the upload file path which works correctly).
Any help would be much appreciated
There you go :
func downloadImage(url : String,
completionHandler: #escaping (Bool?, UIImage?, String?) -> Void) -> Void
{
var success : Bool = false
var img : UIImage? = nil
var errorLog : String? = nil
let u = URL(string: url)
let task = URLSession.shared.dataTask(with: u!, completionHandler: { (data, response, error) in
if error != nil
{
errorLog = error?.localizedDescription
completionHandler(success, img, errorLog)
}
else
{
success = true
img = UIImage(data: data!)
completionHandler(usuccess, img, errorLog)
}
})
task.resume()
}
Get URL using :
imgReference.downloadURL { (url, error) in
guard let url = url else { return }
urlString = url.absoluteString
//do something with the urlString (such as download image)
}
Realized the error was in headers that were included when uploading the image:
I had originally listed the following with the upload, by commenting them out I was able to successfully download with SDWebImage and the suggestion from vbuzze.
let uploadMetadata = StorageMetadata()
uploadMetadata.contentType = "image/jpeg"
uploadMetadata.customMetadata = ["Profile Name" : currentUser.displayName] as? [String : String]
Related
func uploadProfileImage(_ image:UIImage, completion: #escaping ((_ url:URL?)->())) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let storageRef = Storage.storage().reference().child("user/\(uid)")
let imageData = image.jpegData(compressionQuality: 0.75)
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData!, metadata: metaData)
//Get url
let imageRef = storageRef
imageRef.downloadURL { url, error in
if let error = error {
print(error.localizedDescription)
}else{
print(url.absoluteString)
}
}
}
The code inside will not execute, it will skip everything and act like there is no code stored. I am unsure how completions work but I would imagine it would either print the error or print the url but it does neither. When I set a breakpoint here it doesn't even bother checking if error = error. Is there something I did wrong or anyone else having the same problem?
imageRef.downloadURL { url, error in
if let error = error {
print(error.localizedDescription)
}else{
print(url.absoluteString)
}
}
Your putData:metadata:completion: doesn't have completion block. You can access to download URL after upload so putData should be look like this.
storageRef.putData(imageData!, metadata: metaData) { (metadata, error) in
guard let metadata = metadata else {
// an error occurred!
return
}
// Metadata contains file metadata such as size, content-type.
let size = metadata.size
// You can only access to download URL after upload.
let imageRef = storageRef
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// an error occurred!
return
}
}
}
Your API call is asynchronic so that why you need instead of print actually use completion block to return the result (error or value).
When using an image picker to grab the image selected and place in firebase storage I want to be able to download the URL and take place of the profile image inside the app. Unfortunately, when the process reaches the URLSession in the script nothing happens. There is not an error display nor does it dispatchQueue. The app will not crash but just skip over everything. Any ideas or suggestions to a code fix?
if let profileImageUploadedData = self.profileImage.image, let uploadData = profileImage.image?.jpegData(compressionQuality: 0.1)
{
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil
{
print("Downdloading putData Error: \(error!.localizedDescription)")
return
}
storageRef.downloadURL(completion: { (url, error) in
if error != nil
{
print("DownloadURL ERROR \(error!.localizedDescription)")
return
}
if let profileImageUrl = url?.absoluteString
{
print("Profile image uploading...")
let values = ["profileImageUrl": profileImageUrl]
let url = URL(fileURLWithPath: profileImageUrl)
URLSession.shared.dataTask(with: url) { (data, response, error) in // ERROR OCCURING NEED TO FIX
if error != nil
{
print("* URL SESSIONS ERROR: \(error!)")
return
}
DispatchQueue.main.async
{
print("Trying to register profile")
self.registerUserIntoDatabaseWithUID(uid: uid, values: values as [String : AnyObject])
self.profileImage.image = UIImage(data: data!)
print("Dispatch: \(data!)")
}
print("Profile image successfull uploaded to storage")
}
}
})
}).resume()
print("** Profile Image Data Uploaded:\(profileImageUploadedData)")
}
}
func registerUserIntoDatabaseWithUID(uid: String, values: [String: AnyObject])
{
print("Registering to database")
let dataReference = Database.database().reference(fromURL: "URL String")
let usersReference = dataReference.child("users").child(uid)
usersReference.updateChildValues(values, withCompletionBlock: { (err, reference) in
if err != nil
{
print(err!)
return
}
// self.profileImage.image = values["profileImageUrl"] as? UIImage
// self.fetchProfileImage()
self.dismiss(animated: true, completion: nil)
print("Saved user sussccessfully in database")
})
}
}
This is a big question that is actually asking for a number of different answers so let's just focus on one;
How to authenticate a user, get a url stored in Firebase Database that
references an image stored in Firebase Storage, then download that
image.
Here we go
First - authenticate a user
Auth.auth().signIn(withEmail: user, password: pw, completion: { (auth, error) in
if let x = error {
//handle an auth error
} else {
if let user = auth?.user {
let uid = user.uid
self.loadUrlFromFirebaseDatabase(withUid: uid)
}
}
})
now the user is authenticated, get the image location url from Firebase Database
func loadUrlFromFirebaseDatabase(withUid: String) {
let thisUserRef = self.ref.child("users").child(withUid)
let urlRef = thisUserRef.child("url")
urlRef.observeSingleEvent(of: .value, with: { snapshot in
if let url = snapshot.value as? String {
self.loadImageUsingUrl(url: url)
} else {
print("no image for user")
}
})
}
Now that we have the location of the image in Firebase Storage, get it
func loadImageUsingUrl(url: String) {
let storage = Storage.storage()
let imageRef = storage.reference(forURL: url)
imageRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print("error downloading \(error)")
} else {
if let image = UIImage(data: data!) {
//do something with the image
} else {
print("no image")
}
}
}
}
I am trying to download multiple images to display in a collection view cell. But downloading just one exceeds the download size. If I upgrade the download size to a higher value the app crashes after 3 or more images are downloaded. How can I download the images and show them on my collection view effectively?
This is my code to upload:
func uploadImage(_ image: UIImage, uid: String,categoryIndex:Int, spotIndex:Int,completion: #escaping ((_ url: URL?) ->())) {
let storageReference = Storage.storage().reference().child("user/\(uid)/\(categoryIndex)/\(spotIndex).jpg")
guard let imageData = UIImage(data: image.jpegData(compressionQuality: 0.8)!) else { return }
let metaData = StorageMetadata()
metaData.contentType = "img/jpg"
storageReference.putData(imageData.jpegData(compressionQuality: 0.8)!, metadata: metaData, completion: { metaData, error in
if error == nil, metaData != nil {
// success
storageReference.downloadURL(completion: { (url, error) in
guard let downloadURL = url else {
print("ERROR in image link")
return
}
completion(downloadURL)
})
} else {
// Fail
completion(nil)
}
})
}
This is my code to download:
// Download image using the category index and spot index to get the correct image
func downloadImages(folderPath: String, categoryIndex: Int, spotIndex: Int,success: #escaping (_ image: UIImage)->(), failure:#escaping (_ error:Error)->()) {
let reference = Storage.storage().reference(withPath: "\(folderPath)/\(categoryIndex)/\(spotIndex).jpg")
reference.getData(maxSize: (1 * 1024 * 1024)) { (data, error) in
if let error = error {
print(error.localizedDescription)
failure(error)
} else {
if let data = data {
let myImage:UIImage! = UIImage(data: data)
success(myImage)
}
}
}
}
I found the solution. The compressionQuality should be lower than 0.8
I used 0.25 and seems to be working perfectly.
I'm new to IOS development and I'm trying to load an image from a URL, I understand there are some changes between the swift versions.
for some reason I get imageData = nil and I'm not sure why..
private func fetchImage()
{
let url = URL(fileURLWithPath: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png")
if let imageData = NSData(contentsOf: url as URL){
image = UIImage(data: imageData as Data)
}
}
Please check :
private func fetchImage() {
let url = URL(string: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png")!
let task = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error Occurred: \(String(describing: error))")
}
else {
if let imageData = data {
let image = UIImage(data: imageData)
} else {
print("Image file is currupted")
}
}
}
task.resume()
}
You are using the wrong initializer of URL. That one is for filesystem URLs, not for network URLs. This is the working version of your function:
private func fetchImage(){
if let url = URL(string: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png"), let imageData = try? Data(contentsOf: url){
image = UIImage(data: imageData)
}
}
However, you should completely rewrite your function, because Data(contentsOf:) is a synchronous method and hence should only be used to retrieve local files, not files from the internet.
func fetchImage(from url:URL, completion: #escaping (UIImage?)->Void){
URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
guard error == nil, let data = data else {
completion(nil);return
}
completion(UIImage(data: data))
}).resume()
}
fetchImage(from: URL(string: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png")!, completion: {image in
if let image = image {
//use the image
} else {
//an error occured and the image couldn't be retrieved
}
})
I am getting facebook's profile picture and displaying it as the profile picture in my app. Here is the code.
if let user = FIRAuth.auth()?.currentUser{
let photoUrl = user.photoURL
let name = user.displayName
self.FacebookUser.text = name
let storage = FIRStorage.storage()
//refer your particular storage service
let storageRef = storage.reference(forURL: "gs://gsignme-14416.appspot.com")
let profilePicRef = storageRef.child(user.uid+"/profile_pic.jpg")
profilePicRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) -> Void in
if (error == nil){
self.FacebookPic.image = UIImage(data: data!)
}else{
print("Error downloading image:" )
}
})
if(self.FacebookPic.image == nil)
{
var profilePic = FBSDKGraphRequest(graphPath: "me/picture", parameters: ["height": 300, "width": 300, "redirect": false], httpMethod: "GET")
profilePic?.start(completionHandler: {(_ connection, result, error) -> Void in
// Handle the result
if error == nil {
if let dictionary = result as? [String: Any],
let data = dictionary["data"] as? [String:Any],
let urlPic = data["url"] as? String{
if let imageData = NSData(contentsOf: NSURL(string: urlPic)!as URL){
let uploadTask = profilePicRef.put(imageData as Data, metadata: nil) {
metadata, error in
if (error == nil)
{
let downloadurl = metadata!.downloadURL
}
else
{
print("Error in downloading image")
}
}
self.FacebookPic.image = UIImage(data: imageData as Data)
}}}})}
}else{
}
//The END of the Facebook user and picture code
I was able to get it working for a couple days and now it doesn't work anymore, I have gone through it line by line and I honestly can't figure out why it is not working.
I used this code:
func pictureFromFirebase(loginMethod: Int)
{
if loginMethod == 0 //FB
{
var profilePic = FBSDKGraphRequest(graphPath: "me/picture", parameters: ["height":300, "width":300, "redirect":false], httpMethod: "GET")
let profilePicRef = storageRef.child((user?.uid)!+"/profile_pic.jpg")
profilePicRef.data(withMaxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
// Uh-oh, an error occurred!
// but we don't need to do anything yet. Try to download the profile pic
}
if (data != nil)
{
print("no need to download image from facebook")
self.profileImage.image = UIImage (data: data!)
}
else
{
// THIS IS THE BLOCK THAT HAS BEEN MOVED
// WHICH WILL NOW BE EXECUTED IN TWO CONDITIONS -
// 1. AN ERROR IN THE DOWNLOAD
// 2. NO PROFILE PIC AVAILABLE
print("downloading image from facebook")
profilePic?.start(completionHandler: {(_ connection, _ result, _ error) -> Void in
if (error == nil)
{
if let dictionary = result as? [String:Any], let data = dictionary["data"] as? [String:Any],
let urlPic = data["url"] as? String {
if let imageData = NSData(contentsOf: NSURL(string: urlPic)! as URL)
{
let uploadTask = profilePicRef.put(imageData as Data, metadata: nil){
metadata, error in
if (error == nil)
{
let downloadUrl = metadata!.downloadURL
}
else
{
print("error in downloading image")
}
}
self.profileImage.image = UIImage(data: imageData as Data)
}
}
}
})
}
}
}
}
from this post Second If statement gets called before first statement finished in one function and it worked
you just get your facebook profile pic. using this url and put the url in your UIImageview
let profilepicURl = "https://graph.facebook.com/\(user_id_fb)/picture?type=large" //user_id_fb like 1251246454544 your facebook ID