I need to upload a PDF file to Firebase Storage, and I works with my Simulator. But when I test on a real device, I get an infinite loading loop...
I searched for solutions, and read that I have to putData instead of putFile, and add a metadata.contentType.
I followed these 2 steps, but unfortunately it still does not work on a real device...
Here's my function:
func uploadDocs(docsArray : [URL], completionHandler: #escaping ([String]) -> ()){
var uploadedDocsUrlsArray = [String]()
var uploadCount = 0
let docsCount = docsArray.count
for doc in docsArray{
let docName = UUID().uuidString
let storageRef = Storage.storage().reference().child("documents").child(docName)
let metadata = StorageMetadata()
metadata.contentType = "application/pdf"
if let data = NSData(contentsOf: doc) as Data? {
storageRef.putData(data, metadata: metadata) { (_, err) in
if err != nil {
print(err!.localizedDescription)
return
}
storageRef.downloadURL { (url, err) in
if err != nil {
print("Failed to download url:", err!)
return
} else {
uploadedDocsUrlsArray.append(url?.absoluteString ?? "")
uploadCount += 1
print("Number of documents successfully uploaded: \(uploadCount)")
if uploadCount == docsCount{
print("All documents are uploaded successfully")
completionHandler(uploadedDocsUrlsArray)
}
}
}
}
}
}
}
Related
I want to pick video from Images Picker then I want to send video in firebase and retrieve it Please provide code in swift 5, i write code also for sending video on firebase
func downloadImages(folderPath:String,success:#escaping (_ image:UIImage)->(),failure:#escaping (_ error:Error)->()){
// Create a reference with an initial file path and name
let reference = Storage.storage().reference(withPath: "\(folderPath)")
reference.getData(maxSize: (1 * 1024 * 1024 * 1024 * 1024 * 1024)) { (data, error) in
if let _error = error{
print(_error)
failure(_error)
} else {
if let _data = data {
let myImage:UIImage! = UIImage(data: _data)
success(myImage)
}
}
}
}
Upload video On firebase Storage is
func upload(file: URL, completion: #escaping ((_ url : URL?) -> ())) {
let name = "\(Int(Date().timeIntervalSince1970)).mp4"
do {
let data = try Data(contentsOf: file)
let storageRef =
Storage.storage().reference().child("Videos").child(name)
if let uploadData = data as Data? {
let metaData = StorageMetadata()
metaData.contentType = "video/mp4"
storageRef.putData(uploadData, metadata: metaData
, completion: { (metadata, error) in
if let error = error {
completion(nil)
}
else{
storageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
completion(nil)
return
}
completion(downloadURL)
}
print("success")
}
})
}
}catch let error {
print(error.localizedDescription)
}
}
and Get Video From firebase
let reference = Storage.storage().reference().child("Videos").child(folderPath)
reference.getData(maxSize: INT64_MAX) { (data, error) in
if let error = error {
print("Error downloading image data: \(error)")
return
}
reference.getMetadata(completion: { (metadata, metadataErr) in
if let error = metadataErr {
print("Error downloading metadata: \(error)")
return
}
else {
reference.downloadURL { URL, error in
completion(URL)
print(URL)
}
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
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")")
}
}
I want to upload an array of images by user
This is the code when i am uploading a single image to firebase storage refer Code: 1. but i am having problem when i am uploading and array of image. Refer code: 2
Code: 1
func uploadImage(image:UIImage,userId:String,completion:#escaping(_ status:Bool,_ response:String)->Void){
// if status is true then downloadurl will be in response
// Data in memory
guard let data = image.jpegData(compressionQuality: 0.2) else{
completion(false,"Unable to get data from image")
return
}
// Create a reference to the file you want to upload
let riversRef = firebaseStorage.reference().child("images/\(userId).jpg")
// Upload the file to the path "images/rivers.jpg"
let _ = riversRef.putData(data, metadata: nil) { (metadata, error) in
guard let _ = metadata else{
// Uh-oh, an error occurred!
completion(false,error!.localizedDescription)
return
}
// You can also access to download URL after upload.
riversRef.downloadURL { (url, error) in
guard let downloadURL = url else{
// Uh-oh, an error occurred!
completion(false,error!.localizedDescription)
return
}
completion(true,downloadURL.absoluteString)
}
}
}
Code: 2
func uploadPostImages(image:[UIImage],userId:String,completion:#escaping(_ status:Bool,_ response:String)->Void){
let photoDictionary = ["postImages": PostArray.sharedInstance.photosArray as NSArray]
// Create a reference to the file you want to upload
let riversRef = firebaseStorage.reference().child("postImages/\(userId).jpg")
// Upload the file to the path "images/rivers.jpg"
let _ = riversRef.putData(photoDictionary, metadata: nil) { (metadata, error) in
guard let _ = metadata else{
// Uh-oh, an error occurred!
completion(false,error!.localizedDescription)
return
}
// You can also access to download URL after upload.
riversRef.downloadURL { (url, error) in
guard let downloadURL = url else{
// Uh-oh, an error occurred!
completion(false,error!.localizedDescription)
return
}
completion(true,downloadURL.absoluteString)
}
}
}
I am getting this error in code: 2
Cannot convert value of type '[String : NSArray]' to expected argument type 'Data'
anyone who can help me??
To my knowledge something like this should work. Sorry, I am not at an IDE. If this doesn't work leave a comment and I will take a closer look.
func uploadImages(
images:[UIImage],
userId:String,
completion:#escaping(_ status:Bool,_ response:String)->Void)
{
images.enumerated().forEach { (index, image) in
guard let data = image.jpegData(compressionQuality: 0.2) else{
completion(false,"Unable to get data from image")
return
}
// Create a reference to the file you want to upload
let riversRef = firebaseStorage.reference().child("images/\(userId)\(index).jpg")
// Upload the file to the path "images/rivers.jpg"
let _ = riversRef.putData(data, metadata: nil) { (metadata, error) in
guard let _ = metadata else{
// Uh-oh, an error occurred!
completion(false,error!.localizedDescription)
return
}
// You can also access to download URL after upload.
riversRef.downloadURL { (url, error) in
guard let downloadURL = url else{
// Uh-oh, an error occurred!
completion(false,error!.localizedDescription)
return
}
completion(true,downloadURL.absoluteString)
}
}
}
}
This code will upload an array of images
func uploadImages(images:[UIImage], userId:String, completion:#escaping(_ status:Bool, _ response:String)->Void){
guard images.count <= 5 && !images.isEmpty else {return}
convertImagesToData(images: images).enumerated().forEach { (index, image) in
let riversRef = firebaseStorage.reference().child("userAdPostImages/\(userId)\(index).jpg")
let _ = riversRef.putData(image, metadata: nil, completion: { (_ , error) in
if let error = error {
completion(true, error.localizedDescription)
return
}
riversRef.downloadURL(completion: {(url, error) in
if let error = error{
completion(true,error.localizedDescription)
return
}
guard let downloadURL = url else {return}
completion(false,downloadURL.absoluteString)
})
})
}
}
and this will add your post to firebase including array images
func addPost() {
hud.show()
dispatchGroup.enter()
let user = Auth.auth().currentUser
if let user = user {
self.userId = user.uid
}
let location = self.locationTextfield.text ?? ""
let type = self.selectedType ?? ""
let description = self.descriptionTextView.text ?? ""
let adImages = self.adImages
let userId = self.userId ?? ""
let price = priceTextfield.text ?? ""
let userName = self.userName ?? ""
let userProfileImage = self.userProfileImage ?? ""
let userphoneNumber = self.phoneNumber ?? ""
ServerManager.sharedDelegate.uploadImages(images: adImages, userId: userId) { (isError, urlString) in
guard !isError else {
DispatchQueue.main.async {
hud.hide()
}
return
}
self.imagePaths.append(urlString)
print("This is the image path saved \(self.imagePaths)!!!!")
if self.imagePaths.count == adImages.count {
self.dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .global(qos: .background), execute: {
ServerManager.sharedDelegate.addPost(UserProfileImage: userProfileImage, UserName: userName, phoneNumber: userphoneNumber, adImages: self.imagePaths, Price: price, location: location, type: type, Description: description) { _ , message in
DispatchQueue.main.async {
hud.hide()
self.vc?.view.makeToast(message)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.vc?.dismiss(animated: true, completion: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
}
}
return
}
})
}
Thanks to #MichaelWells for help and sorry for late posting the answer
You can encode the dict and then store it as data:
let data = JSONEncoder().encode(photoDictionary)
I am trying to upload two files to firebase storage at the same time. From what I understand this is not possible so what I did was try to upload one first and then the next file once the first is done.
It is working now, however the upload speed is very slow. Is this because of an issue with my code or is it because Im waiting for two uploads as opposed to one.
var index = 0
var urls = [String]()
fileprivate func uploadToServer(data: Any) {
if index == 0 {
let filename = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("posts").child(filename)
storageRef.putData(data as! Data, metadata: nil) { (metadata, err) in
if let err = err {
self.navigationItem.rightBarButtonItem?.isEnabled = true
print("Failed to upload post image:", err)
self.showHUDwithError(error: err)
return
}
storageRef.downloadURL(completion: { (downloadURL, err) in
if let err = err {
print("Failed to fetch downloadURL:", err)
self.showHUDwithError(error: err)
return
}
guard let thisUrl = downloadURL?.absoluteString else { return }
self.urls.append(thisUrl)
self.index = self.index + 1
if self.type == "image" {
// we are done then so do the save to server call here
self.saveToDatabaseWithImageUrl()
} else {
self.uploadToServer(data: self.videoUrl as Any)
}
})
}
return
}
if index == 1 {
let filename = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("posts").child(filename)
storageRef.putFile(from: data as! URL, metadata: nil) { (metadata, err) in
if let err = err {
self.navigationItem.rightBarButtonItem?.isEnabled = true
print("Failed to upload post image:", err)
self.showHUDwithError(error: err)
return
}
storageRef.downloadURL(completion: { (downloadURL, err) in
if let err = err {
print("Failed to fetch downloadURL:", err)
self.showHUDwithError(error: err)
return
}
guard let thisUrl = downloadURL?.absoluteString else { return }
self.urls.append(thisUrl)
self.saveToDatabaseWithImageUrl()
self.index = 0
})
}
}
}