How to properly delete a file, without leaving an orphan

Irena Russeva

Here at SashiDo, we know that owning an app requires keeping neat and up to date database. Therefore you certainly will need to add, update or delete records from time to time. In this tutorial, we will focus on erasing objects and files.

For starters, we would like to share more details on SashiDo’s database and file storage model. The first thing to know is that we do not record the files themselves in your database directly. When an entry, holding a file is created, this file is immediately saved to your S3 bucket. This action will return the file URL, which is actually what is stored in your database. We've chosen this particular model, to give our clients the opportunity to have a lightweight database, with faster performance and lower costs.

In relation to our storage design, If you wish to delete an entry or a simple file from the DB Browser, you may think that it will be removed from the file storage as well. Still, this is not the case. When you delete an entry from the Database through the Dashboard or any of the Parse SDKs, the respective object still remains on the cloud storage. And if you want to remove the particular entry or file from the S3 bucket as well, a different approach will be needed. Don’t you worry, we will guide you through the process.

2018-12-17_2107

To illustrate better the situation, imagine you have a class “Products” in your DB Browser, which contains product characteristics and a field called “Image”, which holds a picture of the respective product. Now, let’s go through the different scenarios of erasing data.

Deleting an object

deleteObj--2-
The proper way to delete an object from your file storage when erasing it from the database is by registering a handler in your cloud code with the beforeDelete or afterDelete Triggers. I would recommend you to use the beforeDelete method, as this way you can easily catch if an error appears while deleting the object from your Database and handle it accordingly.

Assuming the entry is successfully deleted from the database and no error occurs, continue with these two simple steps:

  • Get the name of the removed entry, as you will need it to erase the object from the storage as well.

  • Finish with a simple rest API call to your Parse Server.

You can see how to easily do that in the sample code below:

Parse.Cloud.beforeDelete('Products', async (request, response) => {
   const file = request.object.get('Image'); 
   if (file) {
       const split_url = file.url().split('/');
       const filename = split_url[split_url.length - 1];
       try {
           await Parse.Cloud.httpRequest({
               url: `${process.env.PARSE_SERVER_URL}files/${filename}`,
               method: 'DELETE',
               headers: {
                   'X-Parse-Master-Key': process.env.PARSE_SERVER_MASTER_KEY, 
                   'X-Parse-Application-Id': process.env.PARSE_SERVER_APPLICATION_ID
               }
           });
       } catch (e) {
           const error = `error deleting file ${filename} - ${e.message ||
           e.stack ||
           e.text ||
           JSON.stringify(e)}`;
           console.error(error);
           return response.error(error);
       }
   }

   response.success();
});

Deleting a single file

outputSingle-1
However, deleting an entire object from your database is not the case every time. In different situations, you may need to wipe out just a single file. Well, if removing just the ‘Image’ file from our class ‘Products’ is the desired action, then the approach will be slightly different. Basically, it will involve a simple before/after object comparison, past the deletion of the file from the DB Browser. Let’s dive straight into more details and some code :)

For the situations you want to perform any actions after an object is saved, the afterSave Trigger takes places. You need to register a handler with the afterSave method and compare request.object and request.original. If in the request.object the file is missing, you simply delete it from the file storage via the REST API.

You may get a clear idea on how to proceed from the few lines of code below:

Parse.Cloud.afterSave('Products', async request => {
   const image = request.object.get('Image');
   const imageOriginal = request.original.get('Image');
   if (imageOriginal && image === imageOriginal) {
       return;
   }

   const split_url = imageOriginal.url().split('/');
   const filename = split_url[split_url.length - 1];
   try {
       await Parse.Cloud.httpRequest({
           url: `${process.env.PARSE_SERVER_URL}files/${filename}`,
           method: 'DELETE',
           headers: {
               'X-Parse-Master-Key': process.env.PARSE_SERVER_MASTER_KEY,
               'X-Parse-Application-Id':
               process.env.PARSE_SERVER_APPLICATION_ID
           }
       });
       console.info('File Deleted');
   } catch (e) {
       const error = `error deleting file ${filename} - ${e.message ||
       e.stack ||
       e.text ||
       JSON.stringify(e)}`;
       console.error(error);
   }
});

That’s it! You are all set up now to delete files from the database and the file storage :)

More detailed information can be found in the official Parse documentation, respectively for:

Happy coding!

Irena Russeva

Customer Success Superstar @ SashiDo.

Find answers to all your questions

Our Frequently Asked Questions section is here to help.