StorageReference filePath = employee_photo_profile_reference.child(current_employee_ID+".jpg");
filePath.putFile(result_uri).addOnCompleteListener( new OnCompleteListener<UploadTask.TaskSnapshot>() {
#Override
public void onComplete(#NonNull Task<UploadTask.TaskSnapshot> task) {
if (task.isSuccessful()){
Toast.makeText(activity_setup.this,"Profile Photo stored Successfully.", Toast.LENGTH_SHORT).show();
if (task.getResult() != null){
final String download_url = task.getResult().getStorage().getDownloadUrl().toString();
That line of code is buggy. It's incorrectly using getDownloadUrl(). This is a very common mistake - you can't just call toString() on the result to get a URL.
getDownloadUrl() returns a Task object which you can use to fetch the download URL asynchronously.
The correct usage is demonstrated here: How to get URL from Firebase Storage getDownloadURL
I also suggested reading the documentation.
Related
I see similar questions on this site but most of the questions are related to fetching the download URL for a single uploaded image. Taking help from those posts, now I can get the download URL of a single image.
But I face a problem when I try to get download URL for multiple images uploaded together. I want to do three things...
1. Select three images
2. Upload them to Firebase Cloud Storage
3. Get the URLs of the uploaded images and save them in an ArrayList.
I can do the first two things successfully, but have not managed to achieve the third thing. When I click the "update" button, all images are perfectly stored in Cloud Storage, but show an error when requesting the download URL of all images.
Here is the code for when I click the "update" button:
upload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
progressDialog.setMessage("Uploading .... ");
progressDialog.show();
storageReference = FirebaseStorage.getInstance().getReference().child("Pictures");
int uploadCount = 0;
// imageList is an ArrayList<Uri> which holds the address of selected 3 images.
// imageAddress is an ArrayList<String> where I want to save all downloadUrls of images (each url is saved as a string).
// imagePath is a StorageReference
while(uploadCount < imageList.size()) {
Log.d("UploadCount", uploadCount+"");
Uri uri_Image = imageList.get(uploadCount);
imagePath = storageReference.child(uri_Image.getLastPathSegment());
imagePath.putFile(uri_Image).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
imagePath.getDownloadUrl().addOnSuccessListener(newOnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Uri downloadUri = uri;
imageAddress.add(downloadUri.toString());
Log.d("ImageAddress Size: ", imageAddress.size()+"");
}
});
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(SignOutActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
progressDialog.dismiss();
}
}); //.............
if(uploadCount == (imageList.size()-1)) {
Log.d("Good", "HELLO HELLO");
Toast.makeText(SignOutActivity.this, "Successfully Uploaded", Toast.LENGTH_LONG).show();
upload.setClickable(false);
progressDialog.dismiss();
}
else {
Log.d("BAD", "NOT HELLO "+uploadCount);
}
uploadCount = uploadCount + 1;
}
}
});
Here is the error:
2020-02-15 17:02:26.945 28207-28735/com.example.practiceapplication E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2020-02-15 17:02:26.946 28207-28735/com.example.practiceapplication E/StorageException: {"error": {"code": 404, "message": "Not Found. Could not get object", "status": "GET_OBJECT"}}
java.io.IOException: {"error": {"code": 404, "message": "Not Found. Could not get object", "status": "GET_OBJECT"}}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(com.google.firebase:firebase-storage##19.1.1:433)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(com.google.firebase:firebase-storage##19.1.1:450)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(com.google.firebase:firebase-storage##19.1.1:441)
at com.google.firebase.storage.network.NetworkRequest.performRequest(com.google.firebase:firebase-storage##19.1.1:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(com.google.firebase:firebase-storage##19.1.1:286)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(com.google.firebase:firebase-storage##19.1.1:70)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(com.google.firebase:firebase-storage##19.1.1:62)
at com.google.firebase.storage.GetDownloadUrlTask.run(com.google.firebase:firebase-storage##19.1.1:76)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
2020-02-15 17:02:30.712 28207-28207/com.example.practiceapplication D/ImageAddress Size:: 1
It will be very helpful to me if anyone tells me the correction. Thank you
Your code suffers from using a mix of local variables and shared global variables whilst dealing with asynchronous code and for loops.
In the code above, you use the global variables imagePath, imageAddress and imageList inside a for loop which ultimately is the key cause of that Exception.
Code breakdown
When you click the upload button, your code performs the following steps with errors shown in bold:
Gets the first image's URI
Updates the value of imagePath to point at that image's upload location
Starts the upload of the first image
Logs "NOT HELLO 0"
Gets the second image's URI
Updates the value of imagePath to point at that image's upload location
Starts the upload of the second image
Logs "NOT HELLO 1"
Gets the third image's URI
Updates the value of imagePath to point at that image's upload location
Starts the upload of the third image
Logs "HELLO HELLO" and Toasts "Successfully Uploaded" (not actually finished yet)
[a few moments later]
The first image finishes uploading
The download URL of the third image is requested (which throws the StorageException)
The second image finishes uploading
The download URL of the third image is requested (which throws another StorageException)
The third image finishes uploading
The download URL of the third image is requested (and would work correctly)
Fixes
To fix this, the following things must be done:
Use a local variable copy of imageList
Use a local variable for storageReference
Use a local variable for imagePath, and rename to imageRef to accurately reflect it's type
Rename imageAddress to imageAddressList to accurately reflect it's type (recommended)
Remove the while() loop and use a for iterator instead
Disable the upload button immediately instead of at the end
Upload each image and fetch the download URLs in parallel, without conflicting with each other
Only display "Successfully uploaded" or "Upload failed" messages after the uploads have actually completed
Update imageAddressList only once, rather than asynchronously.
To be done:
Handle activity lifecycle changes
Tap into currentUploadTask and bind it to a view dialog/notification to show file upload progress
Update the UI once all the uploads are done
Updated code
Note: This was typed free-hand - expect a few typos.
upload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
progressDialog.setMessage("Uploading .... ");
progressDialog.show();
upload.setClickable(false); // disable upload button whilst uploading
final StorageReference storageReference = FirebaseStorage.getInstance().getReference().child("Pictures");
final List<Uri> clonedImageList = new ArrayList<>(imageList);
imageList.clear(); // empty old list?
int imageListSize = clonedImageList.size();
List<Task<Uri>> uploadedImageUrlTasks = new ArrayList<>(imageListSize);
for (Uri imageUri : clonedImageList) {
final String imageFilename = imageUri.getLastPathSegment();
Log.d("upload.onClick()", "Starting upload for \"" + imageFilename + "\"...");
StorageReference imageRef = storageReference.child(imageFilename); // Warning: potential for collisions/overwrite
UploadTask currentUploadTask = imageRef.putFile(imageUri);
Task<Uri> currentUrlTask = currentUploadTask
.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
#Override
public Task<Uri> then(#NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
if (!task.isSuccessful()) {
Log.d("upload.onClick()", "Upload for \"" + imageFilename + "\" failed!");
throw task.getException(); // rethrow any errors
}
Log.d("upload.onClick()", "Upload for \"" + imageFilename + "\" finished. Fetching download URL...");
return imageRef.getDownloadUrl();
}
})
.continueWithTask(new Continuation<Uri, Uri>() { // purely for logging to debug, recommended to remove
#Override
public Task<Uri> then(#NonNull Task<Uri> task) throws Exception {
if (!task.isSuccessful()) {
Log.d("upload.onClick()", "Could not get download URL for \"" + imageFilename + "\"!");
throw task.getException(); // rethrow any errors
}
Log.d("upload.onClick()", "Download URL for \"" + imageFilename + "\" is \"" + task.getResult() + "\".");
return task.getResult();
}
});
uploadedImageUrlTasks.add(currentUrlTask);
}
// At this point, all the files are being uploaded in parallel
// Each upload is tracked by the tasks in uploadedImageUrlTasks
Tasks.whenAllComplete(uploadedImageUrlTasks)
.addOnCompleteListener(new OnCompleteListener<List<Task<Uri>>>() {
#Override
public void onComplete(#NonNull List<Task<Uri>> tasks) {
int tasksCount = tasks.size();
List<Uri> failedUploads = new ArrayList<>();
imageAddressList.clear(); // empty old entries?
for (Task<Uri> task : tasks) {
if (task.isSuccessful()) {
successCount++;
Uri downloadUri = task.getResult();
imageAddressList.add(downloadUri.toString());
} else {
Uri imageUri = clonedImageList.get(tasks.indexOf(task));
failedUploads.add(imageUri);
Log.e("upload.onClick()", "Failed to upload/fetch URL for \"" + imageUri.getLastPathSegment() + "\" with exception", task.getException()); // log exception
}
}
progressDialog.dismiss(); // dismiss upload dialog
if (failedUploads.size() > 0) {
Toast.makeText(SignOutActivity.this, failedUploads.size() + "/" + tasksCount + " uploads failed.", Toast.LENGTH_LONG).show();
// TODO: Do something with list of failed uploads such as readd to the now empty upload list
imageList.addAll(failedUploads);
upload.setClickable(true);
} else {
Toast.makeText(SignOutActivity.this, "Successfully uploaded all " + tasksCount + " files.", Toast.LENGTH_LONG).show();
}
// TODO: Now that imageAddressList has been updated, update the UI - e.g tell recycler view to refresh
}
});
}
});
I'm trying to add data to a document to Firebase Firestore. I've added a collection named users to it. Also the read/write permissions are open for now. I'm following this doc. And I'm not able to add data to document.
Here is what I'm trying to do:
private void getNewUserSnapShot() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
FirebaseUser user = firebaseAuth.getCurrentUser();
Log.d(TAG, "getNewUserSnapShot: user_uid: " + user.getUid());
DocumentReference user_doc_ref = db.collection("users").document();
Log.d(TAG, "getNewUserSnapShot: document ref: " + user_doc_ref.getId());
Map<String, Object> user_data = new HashMap<>();
user_data.put("name", user.getDisplayName());
user_data.put("email", user.getEmail());
user_data.put("profile_url", user.getPhotoUrl());
Log.d(TAG, "getNewUserSnapShot: user_data: " + user_data.toString());
user_doc_ref
.set(user_data)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Toast.makeText(LoginActivity.this, task.toString(), Toast.LENGTH_LONG).show();
if (task.isSuccessful()) {
Log.d(TAG, "getNewUserSnapShot: success");
} else {
Log.d(TAG, "getNewUserSnapShot: failed");
}
}
});
}
In Logs I see only these (neither the log for failure nor for success):
2020-03-04 19:48:47.489 30744-30744/com.example.expenditure D/LoginActivity: getNewUserSnapShot: user_uid: iXOzfju6kORnhuUND8zFCPTzxY93
2020-03-04 19:48:47.499 30744-30744/com.example.expenditure D/LoginActivity: getNewUserSnapShot: document ref: 7AluPzcYMLzDKLh8YtBt
2020-03-04 19:48:47.499 30744-30744/com.example.expenditure D/LoginActivity: getNewUserSnapShot: user_data: {profile_url=https://someurl/security/reasons, name=Nikhil Wagh, email=null}
And when I see firebase console, I can't find the document with ID 7AluPzcYMLzDKLh8YtBt, according to logs which should have been created.
There is a similar question: Unable to add information to Cloud Firestore But it doesn't have right answers.
Can someone help. What am I doing wrong?
The issue was user.getPhotoUrl() returns url, but Firestore doesn't support urls. The url needs to be casted as a string, and then it works.
Cast your urls to string before adding data to Firestore.
I'm creating a system for storing multiple image links in firestore, and it's been working, I select from the gallery and upload them as List it is in the order that it is going to the bank (firestore ), because it matters to me, I select for example the images 1,2,3, and when I save their link in the firestore it gets 2,1,3 or 3,1, 2 or 3,2,1 never in the order I loaded, it seems to be according to the lightest image, as saved according to the position I loaded? I've been breaking my head with this for a few days.
already to get each image from List.get(i) to save but to no avail.
I'm saving like this in firestore ->
list_img [
0 link_img
1 link_img
2 link_img]
firestore scheme
upload from gallery
GalleryConfig config = new GalleryConfig.Build()
.limitPickPhoto(50)
.singlePhoto(false)
.hintOfPick("this is pick hint")
.filterMimeTypes(new String[]{})
.build();
GalleryActivity.openActivity(Pag_producao_hq.this, reqCode, config);
upload
private void upload_Fotos_selecionadas(Uri uri, final int totalimg, int i) {
Log.i("sdsd77", String.valueOf(i));
final StorageReference ImageFolder = FirebaseStorage.getInstance().getReference().child("imagens");
String nomeImagem = UUID.randomUUID().toString();
final StorageReference imagename = ImageFolder
.child("HQ")
.child(identificadorUsuario)
.child(nomeImagem);
arrayListImageRef.add(ImageFolder); //arraylist of type StorageRef
arrayListImageRef.add(imagename);
imagename.putFile(uri)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
imagename.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String urlConvertida = uri.toString();
lista_url.add(urlConvertida);
if(lista_url.size()==totalimg){
Map<String, Object> new_imagens = new HashMap<>();
new_imagens.put("list_img", lista_url);
db.collection("HQ")
.document(getId())
.collection("Imagens")
.add(new_imagens);
dialog.dismiss();
finish();
}
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
if (!isFinishing()) {
dialog.dismiss();
}
Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
Cloud Firestore does not list document as the same order of insertion. Auto IDs are not time related. You will need to add a timestamp field and query your data using order by this field.
Hello I am trying to upload multiple images, wait for them to return, compile the download uri's into an object and send it back to my activity. I am using this as reference for upload, firebase. So far i have this
private void saveStepWithImages(#NonNull Step step, Callback callback){
if(step.getStepId() == null){
Collection<Image> images = step.getImages().values();
List<Task<Uri>> taskArrayList= new ArrayList<>();
for (Image i: images) {
taskArrayList.add(uploadImageTask(new ImageUtils().StringToBitMap(i.getImageUrl()), i.getImageReference()));
}
Tasks.whenAll(taskArrayList).addOnCompleteListener(task -> {
Uri downloadUri = task.getResult(); // throws an error because task.getResult is void
});
}else{
updateStepInFirebase(step, callback);
}
}
and in my upload images
private Task<Uri> uploadImageTask(final Bitmap bitmap, String prepend){
final StorageReference ref = mStorageRef.child( prepend );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] data = baos.toByteArray();
UploadTask uploadTask = ref.putBytes(data);
bitmap.recycle();
return uploadTask.continueWithTask(task -> {
bitmap.recycle();
return ref.getDownloadUrl();
});
}
Step is a custom object i created it contains a Map of images with a string as the key and the value being an image. My image class looks like this
public class Image implements Parcelable {
private String imageUrl;
private String imageReference;
public void Image(){
}
//Setters and getters here;
}
Any suggestions would be really appreciated. Thanks!
The key for solving this problem is to use Tasks's whenAllSuccess() method:
Returns a Task with a list of Task results that completes successfully when all of the specified Tasks complete successfully.
Insted of Tasks's whenAll() method:
Returns a Task that completes successfully when all of the specified Tasks complete successfully.
Please see more informations about Tasks class.
You can upload multiple files to firebase by nesting all the calls in one array, and adding each call to the Task API of firebase:
Define the reference and an array of tasks
StorageReference mStorageRef = FirebaseStorage.getInstance().getReference();
List<Task> myTasks = new ArrayList<>();
In this example im using a map that contains each file an its corresponding storage destination
for (Map.Entry<String, Attachment> entry : storageRouteMap.entrySet()) {
String path = entry.getKey();
final Attachment localAtt = entry.getValue();
Uri fileUri = localAtt.getMyUri();
I will put each task in the array of tasks, for a file i have three tasks, one for uploading the file, one for getting the url of the storage and one for writing the metadata in the real time database.
final StorageReference ref = mStorageRef.child(path);
ThreadPerTaskExecutor executor = new ThreadPerTaskExecutor();
UploadTask t1 = ref.putFile(fileUri);
myTasks.add(t1);
Task<Uri> t2 = t1.continueWithTask(executor,new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
#Override
public Task<Uri> then(#NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
if (!task.isSuccessful()) {
throw task.getException();
}
return ref.getDownloadUrl();
}
});
myTasks.add(t2);
Task<Void> t3 = t2.continueWithTask(executor,new Continuation<Uri, Task<Void>>() {
#Override
public Task<Void> then(#NonNull Task<Uri> task) throws Exception {
if (!task.isSuccessful()) {
throw task.getException();
}
Attachment uploadAtt = new Attachment();
uploadAtt.name = localAtt.name;
uploadAtt.url = task.getResult().toString();
uploadAtt.type = localAtt.type;
String idAtt = UtilFirebase.getAttachmentReference().push().getKey();
UtilLog.LogToConsole(TAG," => "+postId+" => "+uidAuthor+" =>"+idAtt);
return UtilFirebase.getAttachmentReference()
.child(postId)
.child(uidAuthor)
.child(idAtt)
.setValue(uploadAtt);
}
}).continueWith(executor,new VideoTransaction(communityId,localAtt.size,localAtt.type));
myTasks.add(t3);
}
Finally i will see if all the tasks where completed or if there was an error, either way this will communicate the result to the main thread.
Task finish = Tasks.whenAll((Collection) myTasks);
finish.addOnCompleteListener(new ThreadPerTaskExecutor(), new OnCompleteListener() {
#Override
public void onComplete(#NonNull Task task) {
if (task.isSuccessful()) {
callback.onComplete();
} else {
callback.onError(task.getException().toString());
}
}
});
I want to let the user share a product from my app by clicking a button and sending other potential users links like
www.myapp.com/offer/123
there, "123" must be generated at the moment the user click the button in order to, later in time, hanle it with
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
Uri deepLink;
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
but unfortunetly I am unable to pass a parameter.
String link = "http://www.myapp.com/offer/123";
Task<ShortDynamicLink> shortLinkTask = FirebaseDynamicLinks.getInstance().createDynamicLink()
.setLink(Uri.parse(link))
.setDynamicLinkDomain("fgd3e.app.goo.gl")
.buildShortDynamicLink()
.addOnCompleteListener(this, new OnCompleteListener<ShortDynamicLink>() {
#Override
public void onComplete(#NonNull Task<ShortDynamicLink> task) {
if (task.isSuccessful()) {
// Short link created
Uri shortLink = task.getResult().getShortLink();
Uri flowchartLink = task.getResult().getPreviewLink();
Can someone teach me how to create a dynamic link at runtime with custom parameters in order to re direct the target user to specific product detail?
SHORT ANSWER: Using query parameters instead of path variables you could use the getQueryParameter method from the Uri object returned by pendingDynamicLinkData.getLink()
What i've been doing is using query parameters instead of path variables.
Instead of sending http://www.myapp.com/offer/123, i'm sending something like http://www.myapp.com/?offer=123
To add parameters dynamically i'm just concatenating strings: "http://www.myapp.com/?offer=" + myValue
This URL is in turn a query parameter of the dynamic link created in firebase:
String url = "https://YourDynamicLinkIdentifier.app.goo.gl/?link=https://myapp.com?offer="
+ myOfferVar
+ "&apn=com.your.apn"; // << Dont forget to change this too
And this resulting URL is the one i send to the url shortener.
Then in the callback onSuccess(PendingDynamicLinkData pendingDynamicLinkData) call getLink() of pendingDynamicLinkData as you're already doing.
Now that you have a Uri object, you can easily get the parameter by calling the method getQueryParameter("offer").
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
String offerKey = deepLink.getQueryParameter("offer");
NOTE: In case you still prefer to use the path variable, you could get the last segment of the Uri path. See How to obtain the last path segment of an uri
You need to use long deep link to send parameters.
Example:
1) link for opening the app by google play testing url:
https://xx.page.link/?link=https://xx.com/invitation/?id=2&apn=com.xx.app&afl=https://play.google.com/apps/testing/com.xx.app
2) receving the parameter:
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
Uri deepLink = null;
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
String paramValue = deepLink.getQueryParameters("id").get(0)); // it will get "2" as a value
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w("Splash", "getDynamicLink:onFailure", e);
}
});