Organize your Node. js upload Application

Organize your Node. js upload Application

This is a continuation of the How to Add Images and pdf files to your Node.js Application Using Cloudinary and Multer article.

In the previous article, we were able to successfully upload files in the image and pdf format just like we wanted.

However, we ran into several issues that made our code messy and error-prone.

Some of the issues we encountered and which we hope to resolve in this article were -:

i) Duplicate copies of files were uploaded

ii) Submitting the POST form without necessarily adding an image or pdf file threw a file path error.

Erasing Duplicate Copies of Uploaded file

The image below is a pictorial representation of what we aim to resolve. I selected the image displayed from my library to test my upload POST request but got the same images uploaded twice.

An Image file containing two duplicate images

Now here's the question, why are we getting a duplicate copy of uploaded files?

Okay, let's try something, on the views folder, go to the upload.ejs file

In the ./views/upload.ejs

Comment out the iframe src attribute by adding <!-- tag before the code and --> tag after the code.

<!DOCTYPE html>
<html lang="en">
    <head>

        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Uploading Images and Pdf Files with Multer and Cloudinary">
        <meta name="keywords" content="upload, images, file, pdf, cloudinary, multer, mongodb">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"/>
        <!-----Title of Page------->
        <title>Upload Files with Multer and Cloudinary </title> 
    </head>
    <body>
     <section>
         <% for (let im of upload) { %>
        <h5><%= im.title %></h5>
        <img src="<%= im.image%>" class="" style="width:50rem; height:40rem;" />
        <!--<iframe src="<%= im.pdfUrl %>" width="100%" height="500"></iframe>--->
         <% } %>
      </section>
    </body>
    </html>

Now save and try to upload a file, an image file only.

A single image file

So here's the result, we have a single uploaded file like we wanted.

The reason for the duplicate images is that the upload form is linked to both the iframe src and image src attributes in the upload ejs template. So when uploading a file, the browser assumes that you are uploading both the image and pdf file. Commenting out the iframe element erased this notion. Now, we have a single element file which our upload forms look up to.

However, this is not how we want our code to be, erasing an integral part of our code.

Let's add a conditional to our code

In the .views/upload.ejs

<!DOCTYPE html>
<html lang="en">
    <head>

        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Uploading Images and Pdf Files with Multer and Cloudinary">
        <meta name="keywords" content="upload, images, file, pdf, cloudinary, multer, mongodb">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous"/>
        <!-----Title of Page------->
        <title>Upload Files with Multer and Cloudinary </title> 
    </head>
    <body>
     <section>
         <% for (let im of upload) { %>
        <h5><%= im.title %></h5>
 <% if (im.image) { %>
        <img src="<%= im.image%>" class="" style="width:50rem; height:40rem;" />
 <% } else if (im.pdfUrl) { %>
        <iframe src="<%= im.pdfUrl %>" width="100%" height="500"></iframe>
         <% } %>
         <% } %>
      </section>
    </body>
    </html>

We added a simple conditional in between our codes; if the uploaded file is an image, work with the image src attribute, else, work with the pdf iframe src attribute.

Test your code.

Now to the next issue.

Submit the Post Form (Text Only) Without Uploading any Image or File

You almost will not find any social media application with the upload files feature where a user cannot bypass the upload file feature and submit a post form with the text-only feature.

Let's test our code to understand the error we are getting when we try to submit the form without adding any file.

I know you just got an error as I did.

TypeError: Cannot read properties of undefined (reading 'path').

Remember in our multer middleware file, we had ''path" as a variable name which carries file extensions for multer to work as it should.

In the upload.js file in the controllers' folder, we defined the uploadFile POST route in a way that requires that the path MUST have a file property, that is, a file extension must be uploaded to the Cloudinary before a successful response from the server can be made.

So how do we resolve this, that the path extension need not necessarily have a file in it for our server to work?

Let's add a conditional in the uploadFile path where we called the multer path.

In .controllers/upload.js

uploadFile: async (req, res) => {
    try {
      // Upload image to cloudinary
      if (req.file !==  undefined) {
      const result = await cloudinary.uploader.upload(req.file.path);

        //add response to model objects
        await Upload.create({
            title: req.body.title,
           image: result.secure_url,
           pdfUrl: result.secure_url,
            cloudinaryId: result.public_id,
          });
        } else { 
          const result =  String;  
          await Upload.create({
            title: req.body.title,
           image: result.secure_url,
           pdfUrl: result.secure_url,
            cloudinaryId: result.public_id,
          });
        }      

console.log("File has been added!");
     res.redirect("/uploadFile")
    } catch (err) {
    console.log(err);
    }
  }

What we did above was to add a conditional to our post file. If the file path is undefined, that is empty, which would be in our case, let our result variable be simply a String. An empty String to be precise.

Then in the .model/Upload.js, change the required property of the files to false

image: {
    type: String,
    require: false,
  },
  pdfUrl: {
    type: String,
    required: false
  }

That is, we are telling the controller that image and pdf file is no longer necessary to be uploaded for the request to be successful.

Now text your code.

Well here is mine..

You can find the source code here

Congratulations, you can upload files with multer and cloudinary, and also manipulate your code to make file upload optional.