Parse Server: Remove old profile image associated with a user - java

I am using File System Storage adapter to save uploaded files on the parse server.
In my app each user can have profile photo. when the user wants to change his photo, the old one should be deleted from the server. But the old image remains unchanged. It leads to fill the server storage after some time. Here is my code:
public void update (Uri uri)
{
ParseUser user = ParseUser.getCurrentUser();
if(uri!=null){
InputStream iStream=getContentResolver().openInputStream(uri);
byte[]image=Helper.getBytes(iStream);
ParseFile file=new ParseFile("profile.png",image);
file.saveInBackground();
user.put("photo",file);
user.saveInBackground();
}
}

Unfortunately Android SDK does not have a function to delete the file but you can do that using Cloud Code Functions or maybe a trigger. Something like this should solve your problem:
Parse.Cloud.beforeSave('_User', ({ original, object }) => {
if (original.get('photo').url() !== object.get('photo').url()) {
original.get('photo').destroy();
}
});

You should propably delete the line "file.saveInBackground();".
Because its runs in background. And when you put that file in user object saving file is not complete and parse server will upload same file to server again with the user object. and You will end having two duplicate files.
Change your code to this:
public void update (Uri uri)
{
ParseUser user = ParseUser.getCurrentUser();
if(uri!=null){
InputStream iStream=getContentResolver().openInputStream(uri);
byte[]image=Helper.getBytes(iStream);
ParseFile file=new ParseFile("profile.png",image);
user.put("photo",file);
user.saveInBackground();
}
}
With this code you upload file only once

Related

Application gives data from deleted database file in Android

I have been working on getting my database backing up to work and I have reached a point where I am not sure what to do.
Basically at first the application opens a Login activity, the user logs in and their database file (if it exists) is downloaded from the Firebase Storage, and then the application navigates to the MainActivity.
In the MainActivity I call a method that sends the user's database file to Firebase Storage. I tried to manage the process by closing the database but since i couldn't fix an error of "E/ROOM: Invalidation tracker is initialized twice :/.", then I found an answer to use a checkpoint (Backup Room database). Now I implemented the forced checkpoint method.
(MarkerDao)
#RawQuery
int checkpoint(SupportSQLiteQuery supportSQLiteQuery);
(MarkerRepository)
public void checkPoint(){
Thread thread= new Thread(() -> markerDao.checkpoint(new SimpleSQLiteQuery("pragma wal_checkpoint(full)")));
thread.start();
}
(ViewModel)
public void setCheckpoint(){
repository.checkPoint();
}
(Database back-up method in the MainActivity)
private void copyDbToFirebase(){
String currentDBPath = "/data/data/"+ getPackageName() + "/databases/locations_table";
File dbBackupFile = new File(currentDBPath);
if (dbBackupFile.exists()){
markerViewModel.setCheckpoint();
// create file from the database path and convert it to a URI
Uri backupDB = Uri.fromFile(new File(currentDBPath));
// Create a StorageReference
StorageReference dbReference = storageRef.child("users").child(userId).child("user database").child("locations_table");
// Use the StorageReference to upload the file
if (userId != null){
dbReference.putFile(backupDB).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Log.d(TAG, "onSuccess: "+4 + taskSnapshot);
Toast.makeText(getApplicationContext(), "Database copied to Firebase 4", Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: "+ e.getMessage());
}
});
}
}
}
If the user logs out, then the files in the "/data/data/"+ getPackageName() + "/databases/" are deleted, which I have manually confirmed by looking at the databases folder of the application.
My issue is that after the databases are deleted and a new user logs in, then the previous database data remains but when I manually check the app's data folder, then the /databases/ folder shows that the files were deleted and a new file is created but it doesn't show any WAL or SHM files and also I get the data of another database which is created when the application first runs, but that file is also not shown in the databases/ folder.
Can anyone explain why the folder doesn't show the files that should be present, where is the application getting the data that was deleted and how to fix it.
Edit: My application has multiple Room databases and I just realized that all the data is still readable after the files were deleted.
The method to delete the database files
private boolean deleteDatabaseFiles(File path) {
if(path.exists() ) {
File[] files = path.listFiles();
for(int i=0; i<files.length; i++) {
if(files[i].isDirectory()) {
deleteDatabaseFiles(files[i]);
}
else {
files[i].delete();
}
}
}
return true;
}
If you are using the same exact RoomDatabase object simply building another one over the same object will prevent any hold over cached data from showing up. I've tested this using multiple database swaps large and small and there is no bleed over.
If you are using a new Instance of the RoomDatabase object for every login try closing the old one after the user logs out. Room will typically close when not needed but if you need it to happen immediately, manually closing it is your best bet.
roomDb.getOpenHelper().close();

How to get the download url from Firebase Storage?

I want to get the Download Url from uploadTask.addOnProgressListener method of Firebase. How can I get the Download Url using following code?
UploadTask uploadTask = storageRef.putBytes(data);
uploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>()
{
#Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot)
{
Log.d("aaaaasessin",""+taskSnapshot.getTask().getResult());
}
});
I used taskSnapshot.getTask().getResult() but that is not working.
Edit Aug 22th 2019:
There is a new method recently added to StorageReference's class in the Android SDK named list().
To solve this, you need to loop over the ListResult and call getDownloadUrl() to get the download URLs of each file. Remember that getDownloadUrl() method is asynchronous, so it returns a Task object. See below for details. I have even written an article regarding this topic called:
How to upload an image to Cloud Storage and save the URL in Firestore?
In order to get the download url, you need to use addOnSuccessListener, like in the following lines of code:
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String url = uri.toString();
//Do what you need to do with url
}
});
}
});
As in the Firebase release notes on May 23, 2018 is mentioned that:
Cloud Storage version 16.0.1
Removed the deprecated StorageMetadata.getDownloadUrl() and UploadTask.TaskSnapshot.getDownloadUrl() methods. To get a current download URL, use StorageReference.getDownloadUr().
So now when calling getDownloadUrl() on a StorageReference object it returns a Task object and not an Uri object anymore.
Please also rememeber, neither the success listener nor the failure listener (if you intend to use it), will be called if your device cannot reach Firebase Storage backend. The success/failure listeners will only be called once the data is committed to, or rejected by the Firebase servers.

Firebase storage onSuccess not being called when image is added

I am trying to save data to firebase with the following structure
root
--clothing
----clothingImgDownloadUrl
------title
------category
As I understand I need to make a reference to the path in my database where I want to save the new data.
FirebaseApp app = FirebaseApp.getInstance();
assert app != null;
mDatabase = FirebaseDatabase.getInstance(app);
mClothingRef = mDatabase.getReference("clothing");
I want to add new clothing using the clothing reference created above
mClothingRef.child(clothingImgDownloadUrl).setValue(clothing);
However the code above is only ran once the clothing image is uploaded to firebase storage and its download url is returned to the app where it can be used as the unique identifier for the entries in the firebase database. Below is the code to upload the image to firebase storage.
FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageRef = storage.getReference(FIREBASE_STORAGE_CLOTHING_REFERENCE);
StorageReference photoRef = storageRef.child(mSelectedImageUri.getLastPathSegment());
photoRef.putFile(mSelectedImageUri)
.addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// When the image has successfully uploaded, we get its download URL
Log.d("ADMIN_ACTIVITY", "Called");
Uri downloadUrl = taskSnapshot.getDownloadUrl();
assert downloadUrl != null;
String imageUrl = downloadUrl.toString();
Clothing clothing = new Clothing(imageUrl, title, mCategory);
mClothingRepository.addClothing(clothing);
}
});
When the app is ran and I add a clothing item from the admin part of the app, the image is indeed added to the firebase storage, however the Log call in the last code snippet is not called. Surely if the image is added then the onSuccess listener should be called and then the addClothing method should be ran.
Why is the onSuccess listener not being called and am I saving to the real time database correctly?
You are using the activity-scoped form of addOnSuccessListener(). The documentation notes that:
The listener will be automatically removed during onStop()
The most likely explanation for the listener not being called is that the activity passed as the first argument (this) has completed and its onStop() method called. To confirm that this is the case, remove the this argument and see if the listener is called.

example using uploadDirectory from android to S3

Will someone please provide an example for uploading a bunch of photos to S3 using uploadDirectory? Say I have 300 photos in a directory named “special_photos” on my android device. And I want to upload all of these photos to Amazon S3. I figure uploadDirectory may be the best method for doing this. But being new to Amazon cloud, I don’t know how I might do it. All I have gleaned so far is that the method executes asynchronously and so can be called from the main thread. I keep finding php codes on the internet. But I don’t use PHP. Does anyone have a complete working example they don’t mind sharing with the community? I am using the SDK via gradle on Android Studio. Also, is there some kind of callback for knowing when all the photos have been uploaded? Say for instance I want to delete the photos and the directory once they have been uploaded.
There is no uploadDirectory but there is Multipart Upload. This will do your large data upload to S3. As stated HERE, the Multipart Upload Docs say:
Using the list multipart uploads operation, you can obtain a list of multipart uploads in progress. An in-progress multipart upload is an upload that you have initiated, but have not yet completed or aborted. Each request returns at most 1000 multipart uploads. If there are more than 1000 multipart uploads in progress, you need to send additional requests to retrieve the remaining multipart uploads.
To address the callback, there is a completion called once all of the TransferUtility items are uploaded. This open source adds listeners applied to the upload function. I would recommend breaking up your calls to 30 at a time, then delete the corresponding photos - in case there is a failure with the upload. There is a success and fail return, so obviously only delete in case of success.
HERE is the AWS documentation for Android Multipart Uploads
HERE is an article that will help migrate & understand the differences between TransferManager and TransferUtility
HERE is a good article on getting started with the Android TransferManager
And HERE is an open source demo - under the S3_TransferManager
Hope this helps!
Update:
The below code is all taken from #awslabs references
Create client:
public static AmazonS3Client getS3Client(Context context) {
if (sS3Client == null) {
sS3Client = new AmazonS3Client(getCredProvider(context.getApplicationContext()));
}
return sS3Client;
}
Create TransferUtility:
public static TransferUtility getTransferUtility(Context context) {
if (sTransferUtility == null) {
sTransferUtility = new TransferUtility(getS3Client(context.getApplicationContext()),
context.getApplicationContext());
}
return sTransferUtility;
}
Use TransferUtility to get all upload transfers:
observers = transferUtility.getTransfersWithType(TransferType.UPLOAD);
Add your records: - you could iterate over the file names in your directory
HashMap<String, Object> map = new HashMap<String, Object>();
Util.fillMap(map, observer, false);
transferRecordMaps.add(map);
This starts everything:
private void beginUpload(String filePath) {
if (filePath == null) {
Toast.makeText(this, "Could not find the filepath of the selected file",
Toast.LENGTH_LONG).show();
return;
}
File file = new File(filePath);
TransferObserver observer = transferUtility.upload(Constants.BUCKET_NAME, file.getName(),
file);
observers.add(observer);
HashMap<String, Object> map = new HashMap<String, Object>();
Util.fillMap(map, observer, false);
transferRecordMaps.add(map);
observer.setTransferListener(new UploadListener());
simpleAdapter.notifyDataSetChanged();
}
This is your completion:
private class GetFileListTask extends AsyncTask<Void, Void, Void> {
// The list of objects we find in the S3 bucket
private List<S3ObjectSummary> s3ObjList;
// A dialog to let the user know we are retrieving the files
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog = ProgressDialog.show(DownloadSelectionActivity.this,
getString(R.string.refreshing),
getString(R.string.please_wait));
}
#Override
protected Void doInBackground(Void... inputs) {
// Queries files in the bucket from S3.
s3ObjList = s3.listObjects(Constants.BUCKET_NAME).getObjectSummaries();
transferRecordMaps.clear();
for (S3ObjectSummary summary : s3ObjList) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("key", summary.getKey());
transferRecordMaps.add(map);
}
return null;
}
#Override
protected void onPostExecute(Void result) {
dialog.dismiss();
simpleAdapter.notifyDataSetChanged();
}
}
You can send a List to your service that is using TransferUtility to upload multiple images. At least that is how I was able to make it work.

Network error from google services in Android

I am developing an app in which i have an integrated google drive. I want to store images captured by the device under a folder in google drive.
By doing so in result.getstatus() method from google service is returning {statusCode=Failed to retrieve item from a network}
I am using following function,
final ResultCallback<DriveIdResult> idCallback = new ResultCallback<DriveIdResult>() {
#Override
public void onResult(DriveIdResult result) {
System.out.println("result.getStatus()----"+ result.getStatus());
System.out.println("result.getStatus() code----"+ result.getStatus().getStatusCode());
if (!result.getStatus().isSuccess()) {
showMessage("Cannot find DriveId. Are you authorized to view this file?");
return;
}
DriveFolder folder = Drive.DriveApi
.getFolder(getGoogleApiClient(), result.getDriveId());
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("NewFolder").build();
folder.createFolder(getGoogleApiClient(), changeSet)
.setResultCallback(createFolderCallback);
}
};
In first println---> I am getting {statusCode=Failed to retrieve item from a network}
In second println--> I am getting 7
Please help me...
I have the same status code when the file is permanently deleted. Remove any cached DriveId / resource id and create the new file.

Categories

Resources