I'm sending a video and the thumbnail Image from the client to Firebase Storage and I save the respective urls in Firestore using this procedure:
fileprivate func sendVideoToDatabase(urlVideo : URL){
ProgressHUD.showProgress(0.0, interaction: true)
guard let uid = Auth.auth().currentUser?.uid else { return }
let videoName = UUID().uuidString
let storageRef = Storage.storage().reference().child("Videos").child("\(videoName).mp4")
let storageThumbnailsRef = Storage.storage().reference().child("thumbnails")
storageRef.putFile(from: urlVideo, metadata: nil) { (metadata, error) in
if let error = error{
print("failed upload video \(error.localizedDescription)")
ProgressHUD.showFailed()
return
}
ProgressHUD.showProgress(0.1, interaction: true)
storageRef.downloadURL { (urlVideo, error) in
if let error = error{
print(error.localizedDescription)
ProgressHUD.showFailed()
return
}
ProgressHUD.showProgress(0.2, interaction: true)
let firestoreMoments = Firestore.firestore().collection("Moments").document(uid).collection("Moments")
ProgressHUD.showProgress(0.3, interaction: true)
self.getImageFromVideo(url: urlVideo!, at: 1) { (imageRetrieved) in
let imageID = "\(UUID().uuidString).jpg"
guard let imageData = imageRetrieved?.jpegData(compressionQuality: 0.35) else { return }
ProgressHUD.showProgress(0.4, interaction: true)
storageThumbnailsRef.child(imageID).putData(imageData, metadata: nil) { (metadata, error) in
ProgressHUD.showProgress(0.5, interaction: true)
if let err = error{
print("impossible to add a thumbnail image: \(err.localizedDescription)")
ProgressHUD.showFailed()
return
}
ProgressHUD.showProgress(0.6, interaction: true)
storageThumbnailsRef.child(imageID).downloadURL { (url, err) in
if let err = err{
print("impossible to download image: \(err.localizedDescription)")
ProgressHUD.showFailed()
return
}
ProgressHUD.showProgress(0.7, interaction: true)
guard let thumbnailUrl = url?.absoluteString else { return }
let docRef = firestoreMoments.document()
ProgressHUD.showProgress(0.8, interaction: true)
docRef.setData(["type" : "video",
"uid": uid,
"videoUrl" : urlVideo!.absoluteString,
]) { (error) in
if let error = error{
print(error.localizedDescription)
ProgressHUD.showFailed()
return
}
ProgressHUD.showProgress(1, interaction: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
ProgressHUD.dismiss()
})
}
}
}
}
}
}
}
Meanwhile I show a progressHUD that prevents any user interaction from the client side until the uploading process is complete. The problem is that it takes a lot of time for a video of decent size (20MB).
I was wondering is there a way to implement an uploading process that goes on in the server side or background? so that the client can go on and perform other tasks meanwhile?
Related
I'm trying to upload a video file using phpickerviewcontroller, but I'm running into an issue uploading the URL to FirebaseStorage. Here is some code:
func uploadVideo(videoURL: URL)
{
let storage = Storage.storage()
let storageRef = storage.reference()
let videoRef = storageRef.child("rPosts/\(uid!)/\(fileID)")
let metadata = StorageMetadata()
metadata.contentType = "video/quicktime"
var videoData: Data = Data()
do
{
videoData = try Data(contentsOf: videoURL)
}
catch
{
print(error.localizedDescription)
return
}
videoRef.putData(videoData, metadata: metadata)
{ (metaData, error) in
guard error == nil else
{
self.errorLabel.text = error!.localizedDescription
return
}
}
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult])
{
dismiss(animated: true, completion: nil)
guard let itemProvider = results.first?.itemProvider else { return }
itemProvider.loadItem(forTypeIdentifier: "com.apple.quicktime-movie", options: nil)
{ (videoFile, error) in
guard error == nil else { return }
let videoFile = videoFile as? URL
DispatchQueue.main.async
{
self.uploadVideo(videoURL: videoFile!)
print(videoFile!)
}
self.uploadedYet = true
}
}
I've tried using .putFile but it keeps on saying
Ensure file URL is not a directory, symbolic link, or invalid url.
When I use .putData it says
The file "..." couldn’t be opened because there is no such file
EDIT:
itemProvider.loadFileRepresentation(forTypeIdentifier: "com.apple.quicktime-movie")
{ (videoURL, error) in
guard error == nil else { return }
print("isbeingcalled") //does not get calleed :(
DispatchQueue.main.async
{
let storageRef = Storage.storage().reference()
let videoRef = storageRef.child("rPosts/\(self.uid!)/\(self.fileID).mov")
let metadata = StorageMetadata()
metadata.contentType = "video/quicktime"
print("run")
videoRef.putFile(from: videoURL!, metadata: metadata)
{ (metaData, error) in
guard error == nil else
{
print(videoURL!)
print(videoRef.fullPath)
self.errorLabel.text = error!.localizedDescription
print(error!.localizedDescription)
return
}
}
}
self.uploadedYet = true
}
I have a multi-selection imagepicker with the intention of allowing the user to select multiple assets, then upload each asset to the database. In the completion handler, I take all the selected assets and pass them to a custom function: uploadImageAssets(assets: [PHAsset], projectRef: DocumentReference), where the upload begins.
In the function, I'm using a for-loop to upload each asset individually. While the assets are being uploaded correctly, not all assets are being uploaded. Lets say I've selected 5 assets... Only 4 will show up in the database, and they'll all be the same image, repeated. Any idea as to why this is happening? Here is my code below:
Image Picker Selection:
#IBAction func uploadProjectTapped(_ sender: Any) {
let imagePicker = ImagePickerController()
imagePicker.settings.selection.max = 10
imagePicker.settings.theme.selectionStyle = .numbered
imagePicker.settings.fetch.assets.supportedMediaTypes = [.image, .video]
imagePicker.settings.selection.unselectOnReachingMax = false
let start = Date()
self.presentImagePicker(imagePicker, select: { (asset) in
print("Selected: \(asset)")
}, deselect: { (asset) in
print("Deselected: \(asset)")
}, cancel: { (assets) in
print("Canceled with selections: \(assets)")
}, finish: { (assets) in
print("Finished with selections: \(assets)")
self.getAssetThumbnail(assets: assets)
}, completion: {
let finish = Date()
print(finish.timeIntervalSince(start))
})
}
And, the function to add them to Firestore:
func uploadImageAsset(assets: [PHAsset], projectRef: DocumentReference) {
let userID = Auth.auth().currentUser?.uid
let db = Firestore.firestore()
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
option.isSynchronous = false
option.isNetworkAccessAllowed = true
option.resizeMode = .exact
option.version = .original
option.deliveryMode = .highQualityFormat
let uniqueImageID = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("project-images").child("\(uniqueImageID).jpeg")
for asset in assets {
let imageSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
manager.requestImage(for: asset, targetSize: imageSize, contentMode: .aspectFill, options: option) { (image, info) in
let uploadData = image?.jpegData(compressionQuality: 0.6)
storageRef.putData(uploadData!, metadata: nil, completion: {
(metadata, error) in
if error != nil {
return
} else {
storageRef.getMetadata(completion: { (metadata, err) in
if let error = err {
print(error)
} else {
storageRef.downloadURL(completion: { (url, err) in
if let error = err {
print(error)
} else {
self.imageAssetURLs.append((url?.absoluteString)!)
guard let url = url?.absoluteString else { return }
projectRef.updateData(["images": FieldValue.arrayUnion([url])], completion: { (err) in
if err != nil {
print(err)
} else {
self.dismiss(animated: true, completion: nil)
}
})
}
})
}
})
}
})
}
}
}
I have a strong feeling that the error lies within this line:
self.imageAssetURLs.append((url?.absoluteString)!)
guard let url = url?.absoluteString else { return }
in func uploadImageAsset(...) the
let uniqueImageID = NSUUID().uuidString
and
let storageRef = Storage.storage().reference().child("project-images").child("\(uniqueImageID).jpeg")
should be inside the loop just before
storageRef.putData(..)
I'm new to firebase and I'm facing a problem here.
I have successfully uploaded my image to firebase storage already, however, I need to get the downloadurl at once and post it to the database. And I'm not sure why it doesn't work within that closure.
The following is my code:-
func CreateNewChatRoom(user: User, caption: String, data: Data){
let filePath = "\(user.uid)/\(Int(NSDate.timeIntervalSinceReferenceDate)).jpg"
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
let reference = storageRef.child(filePath)
reference.putData(data, metadata: metaData) { (metadata, error) in
if let error = error {
print("\(error.localizedDescription)")
return
}
reference.downloadURL(completion: { (url, error) in
if let error = error{
print("Error : \(error.localizedDescription)")
return
}
else{
if let downloadURL = url?.absoluteString {
let idRoom = self.BASE_REF.child("rooms").childByAutoId()
idRoom.setValue(["caption":caption, "thumbnailUrlFromStorage": self.storageRef.child(metadata!.path!).description,"fileUrl": downloadURL])
}
}
})
}
}
I have the same problem, and I found the following solution,
I hope it is useful to someone.
Swift 5
import FirebaseAuth
import FirebaseStorage
import FirebaseFirestore
typealias AddStoryResult = (Bool, Error?) -> Void
typealias UploadImageResult = (StorageMetadata?, Error?) -> Void
class StoryFirebaseAPI {
static let storage = Storage.storage()
static var downloadUrlString: URL?
var image: UIImage?
let photoUri = "smth uri path"
uploadImage(image, photoUriString: photoUri) { (metadata: StorageMetadata?, error: Error?) in
guard let metadata = metadata else {
if let error = error {
print("Error occured: \(error)")
}
completion(false, error)
return
}
// and at this point I have print(downloadUrlString)
completion(true, nil)
}
}
extension StoryFirebaseAPI {
func uploadImage(_ image: UIImage, photoUriString: String, completion: #escaping UploadImageResult) {
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
print("smth error")
completion(nil, nil)
return
}
let storageRef = storage.reference().child(photoUriString)
let imageMetadata = StorageMetadata()
imageMetadata.contentType = "image/jpeg"
storageRef.putData(imageData, metadata: imageMetadata) { (storageMetadata, error) in
if let error = error {
print("Error: \(error.localizedDescription)")
completion(nil, error)
return
}
guard let unwrappedMetadata = storageMetadata else {
print("smth error")
print("Error: \(error?.localizedDescription ?? "")")
completion(nil, error)
return
}
storageRef.downloadURL { url, error in
if let error = error {
print("Error: \(error.localizedDescription)")
completion(nil, error)
return
} else {
if let url = url {
downloadUrlString = url
completion(unwrappedMetadata, nil)
}
}
}
print("Image uploaded successfully")
}
}
}
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")
}
}
}
}
Here is the code that I'm using
#objc func handleSignUp() {
guard let email = emailTextField.text, email.characters.count > 0 else { return }
guard let username = usernameTextField.text, username.characters.count > 0 else { return }
guard let password = passwordTextField.text, password.characters.count > 0 else { return }
Auth.auth().createUser(withEmail: email, password: password, completion: { (user, error) in
if let err = error {
print("Failed to create user:", err)
return
}
print("Successfully created user:", user?.user.uid)
// guard let image = self.plusPhotoButton.imageView?.image else { return }
// guard let uploadData = UIImageJPEGRepresentation(image, 0.3) else { return }
guard let uploadData = self.plusPhotoButton.imageView?.image?.jpegData(compressionQuality: 0.3) else{return}
let filename = NSUUID().uuidString
Storage.storage().reference().child("profile_images").child(filename).putData(uploadData, metadata: nil, completion: { (metadata, err) in
if let err = err {
print("Failed to upload profile image:", err)
return
}
//this code isn't correct for swift4.2
//guard let profileImageUrl = metadata?.downloadURL()?.absoluteString else { return }
func getDownloadURL(from path: String, completion: #escaping (URL?, Error?) -> Void) {
let storageReference = Storage.storage().reference(forURL: "gs://instagramfirebase-60be5.appspot.com")
let storageRef = storageReference.child(path).downloadURL(completion: completion)
}
print("Successfully uploaded profile image")//,profileImageUrl
guard let uid = user?.user.uid else { return }
let dictionaryValues = ["username": username] as [String : Any]//, "profileImageUrl": profileImageUrl
let values = [uid: dictionaryValues]
Database.database().reference().child("users").updateChildValues(values, withCompletionBlock: { (err, ref) in
if let err = err {
print("Failed to save user info into db:", err)
return
}
print("Successfully saved user info to db")
})
})
})
}
Snapshots are as followed :
I am confused about how to get the imageUrl from firebase storage and store it in firebase database in Xcode 10 swift 4.2
Please help, Thank you for your time.
You can get the download url easily by using the following code inside the closure of your image-upload:
storageRef.downloadURL(completion: { (url, error) in
if let err = error{
// error happened - implement code to handle it
print(err)
} else {
// no error happened; so just continue with your code
print(url?.absoluteString) // this is the actual download url - the absolute string
}
Please note: Don't store the downloadURL on its own as Firebase can change tokens of the downloadURL, I'd suggest to always grab a hold of the full storage path.
I hope I was able to help.