Search code examples
firebasereact-nativefirebase-storagereact-native-firebase

How to copy an image over to another reference path in Firebase Storage?


In a react-native app (v61.5) using react-native-firebase (v5), I have an image path that goes something like this:

/message-attachments/{session-id}

i have scheduled message deletion after a certain period of time, during which any deleted message sessions which also have attachments stored at this session-id reference also gets deleted.

the problem is if a session gets archived, the image needs to be moved/duplicated from that message-attachments ref to lets say /{user}/archived-attachments/{session-id} so it persists the scheduled message deletion.

firebase storage is used for this project, and im trying to achieve this from a duplication stand point so the reference path doesnt need to be entirely re-architected. according to the v5 react-native-firebase docs, putFile() can ONLY take a device file path. but since the source of the image is another location in firebase, it seems that wont work. the only thing i can think of which might fit these requirements is to use .downloadFile('${firebase.storage.Native.DOCUMENT_DIRECTORY_PATH}/ok.jpeg') and the in the same redux action use putFile('${firebase.storage.Native.DOCUMENT_DIRECTORY_PATH}/ok.jpeg') to get it to the correct reference path in Storage.

this seems hacky and id prefer to avoid download the image to the users device, is there another way?


Solution

  • It is not good to download and re-upload a file. Theoretically, files may scale big and downloading files several megabytes large and then re-uploading them again is a bad solution.

    Instead, you can create a cloud function that will do the move (copy/delete) operation.

    Firebase function to perform file move (copy/delete)

    const { Storage } = require('@google-cloud/storage');
    
    exports.moveFile = functions.region(region).https.onCall(async (data, context) => {
      // Simple mechanism to secure the function call to logged in users.
      // There are other methods to secure cloud functions using IAM and
      // you should always specify rules to the storage buckets to allow users
      // view manage just their own files.
    
      const { auth } = context || {};
      const { uid } = auth || {};
    
      if (!uid) {
        throw 'Unauthenticated';
      }
    
      const srcBucketName = 'src-bucket';
      const destBucketName = 'dest-bucket'; // Can be the same as source bucket
      // Retrieve the file that the RN app passed to archive.
      // We should also check if it's a valid file.
      const { archiveFile } = data;
      const srcFilename = `path/to/currentFile/${archiveFile}`;
      const destFilename = `${uid}/archived-attachments/{session-id}/${srcFilename}`;
    
      const storage = new Storage();
      storage
        .bucket(srcBucketName)
        .file(srcFilename)
        .copy(storage.bucket(destBucketName).file(destFilename))
        .then(() => {
          console.log(
            `gs://${srcBucketName}/${srcFilename} copied to gs://${destBucketName}/${destFilename}.`
          );
          storage
            .bucket(srcBucketName)
            .delete()
            .then(() => {
              // Done moving file
              return res.status(200);
            })
            .catch(err => {
              console.error('DELETE ERROR:', err);
            });
        })
        .catch(err => {
          console.error('COPY ERROR:', err);
        });
    });
    

    Call the function in React native using @react-native-firebase/functions

    import firebase from '@react-native-firebase/app';
    import '@react-native-firebase/functions';
    
    firebase.app().functions(region).httpsCallable('moveFile')({
      archiveFile: 'some-file.txt'
    }).then(() => {
      // Done moving file
    }).catch(error => {
      // Handle error
    });