0

I am attempting to use Firebase to store and sync several CSV files. Copies of these CSV files are stored in internal storage.

To update them locally, I am looping through all items within the Firebase Storage bucket, comparing the updated times between the two and then if there is a new version, downloading it.

Everything seems to be working perfectly except the metadata fetching from Firebase, the lastupdated time of the Firebase files is always returning a zero? I find this very odd, because it can download the file just fine, meaning there shouldn't be a Firebase rule blocking a metadata fetch request or anything. Is last updated time by default 0 unless is it manually added?

public void updateDataBase(View view){
    FirebaseStorage firebaseStorage = FireBaseStorage.getInstance();
    StorageReference reference = firebaseStorage.getReference().getRoot();

    final File rootpath = new File(getFilesDir(),"database");
    if(!rootpath.exists()){
        Log.i(TAG,"Folder Created: " + rootpath.mkdirs());
    }
    reference.listAll().addOnSuccessListener(new OnSuccessListener<ListResult>() {
        @Override
        public void onSuccess(ListResult listResult) {
            for(final StorageReference item : listResult.getItems()) {
                final File localFile = new File(rootpath, item.getName());
                final long[] onlineFileChangeDate = new long[1];
                item.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
                    @Override
                    public void onSuccess(StorageMetadata storageMetadata) {
                        onlineFileChangeDate[0] = storageMetadata.getUpdatedTimeMillis();
                    }
                });
                Log.i("Settings","LastModified: " + localFile.lastModified());
                Log.i("Settings", "LastModified:" + onlineFileChangeDate[0]);
                if (localFile.lastModified() > onlineFileChangeDate[0]) {
                    Log.i(TAG, "File deleted " + localFile.delete());
                    item.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
                        @Override
                        public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
                            Toast.makeText(settings.this, "Downloaded: " + localFile, Toast.LENGTH_SHORT).show();

                        }
                    });

                }
            }
            Toast.makeText(settings.this, "Update Complete", Toast.LENGTH_SHORT).show();
        }
    }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Log.e(TAG,"Firebase Update Error");
                }
            });
}
River_B
  • 75
  • 11

1 Answers1

1

Retrieving the metadata is an asynchronous operation, which happens in the background, while your main code continues. It's easiest to see what that means by adding a few simple log statements:

reference.listAll().addOnSuccessListener(new OnSuccessListener<ListResult>() {
    @Override
    public void onSuccess(ListResult listResult) {
        for(final StorageReference item : listResult.getItems()) {
            Log.i(TAG, "Before calling getMetadata()");

            item.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
                @Override
                public void onSuccess(StorageMetadata storageMetadata) {
                    Log.i(TAG, "Got metadata");
                }
            });
            Log.i(TAG, "After calling getMetadata()");
        }
    }
})

When run, this code prints:

Before calling getMetadata()

After calling getMetadata()

Got metadata

This is probably not what you expected, but it is working as designed, and it explains exactly why your code doesn't work: by the time your if (localFile.lastModified() > onlineFileChangeDate[0]) runs, the onlineFileChangeDate[0] = storageMetadata.getUpdatedTimeMillis() hasn't set it to a value yet.


The solution for his (and any other problems with asynchronous method calls) is that the code that needs the metadata needs to be inside the onSuccess that gets called with that metadata, or be called from there.

The simplest fix:

reference.listAll().addOnSuccessListener(new OnSuccessListener<ListResult>() {
    @Override
    public void onSuccess(ListResult listResult) {
        for(final StorageReference item : listResult.getItems()) {
            final File localFile = new File(rootpath, item.getName());
            item.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
                @Override
                public void onSuccess(StorageMetadata storageMetadata) {
                    if (localFile.lastModified() > storageMetadata.getUpdatedTimeMillis()) {
                        Log.i(TAG, "File deleted " + localFile.delete());
                        item.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
                            @Override
                            public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
                                Toast.makeText(settings.this, "Downloaded: " + localFile, Toast.LENGTH_SHORT).show();
                            }
                        });
                    }

                }
            });

        }
        Toast.makeText(settings.this, "Update Complete", Toast.LENGTH_SHORT).show();
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        Log.e(TAG,"Firebase Update Error");
    }
});

Also see:

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807