If You Want to See the Progress of the Upload

Cover image for Uploading files in React with Progress bar using Express server

collegewap

Uploading files in React with Progress bar using Express server

You might see many websites where a file needs to be uploaded, like uploading a contour flick while creating a profile.
If the user has a slow network or uploads a huge file, and so they might need to wait for a longer period of time later on clicking on the upload button.
In such cases, it is skilful to show feedback to the user such every bit a progress bar,
rather than having the user stare at the screen and wondering what is happening.

In this tutorial, we will come across how tin we accomplish file upload in React and Express/Node backend with assistance of the multer node library.

Creating the React Project

Starting time, create a folder named react-upload-file-progress-bar and create ii directories client and server within it.
Navigate to the client directory and run the following command to create the client project:

            npx create-react-app              .                      

Enter fullscreen mode Leave fullscreen style

Creating the upload grade

We will be making use of react-bootstrap to style the page and display the progress bar.
So let's install it inside the client project.

            yarn add together bootstrap react-bootstrap                      

Enter fullscreen manner Exit fullscreen mode

Import the bootstrap css in index.js:

                          import              React              from              "              react              "              import              ReactDOM              from              "              react-dom              "              import              App              from              "              ./App              "              import              "              bootstrap/dist/css/bootstrap.min.css              "              ReactDOM              .              render              (              <              React              .              StrictMode              >              <              App              />              </              React              .              StrictMode              >,              certificate              .              getElementById              (              "              root              "              )              )                      

Enter fullscreen style Go out fullscreen mode

Now add the following code to App.js

                          import              {              Container              ,              Row              ,              Col              ,              Form              ,              Push button              }              from              "              react-bootstrap              "              role              App              ()              {              return              (              <              Container              >              <              Row              >              <              Col              lg              =              {              {              bridge              :              4              ,              outset              :              3              }              }              >              <              Form              action              =              "http://localhost:8081/upload_file"              method              =              "post"              enctype              =              "multipart/course-data"              >              <              Form              .              Group              >              <              Form              .              File              id              =              "exampleFormControlFile1"              label              =              "Select a File"              proper name              =              "file"              />              </              Form              .              Group              >              <              Form              .              Grouping              >              <              Button              variant              =              "info"              type              =              "submit"              >              Upload              </              Button              >              </              Form              .              Group              >              </              Class              >              </              Col              >              </              Row              >              </              Container              >              )              }              export              default              App                      

Enter fullscreen mode Leave fullscreen fashion

In the above code, nosotros have created a grade with file input and an upload push button.
We have styled the form using bootstrap components.

Now if you start the application and open up http://localhost:3000 in your browser, yous would see a page as shown below:
Form

Binding the form with backend API

We volition exist making utilize of Axios to make API calls (upload file in our instance). So let's go alee and install it:

            yarn add axios                      

Enter fullscreen fashion Get out fullscreen fashion

Within the src directory, create a subfolder named utils and create a file named axios.js with the post-obit contents:

                          import              axios              from              "              axios              "              const              axiosInstance              =              axios              .              create              ({              baseURL              :              "              http://localhost:8081/              "              ,              })              export              default              axiosInstance                      

Enter fullscreen mode Get out fullscreen mode

This creates an instance of Axios and this instance can exist reused wherever required and
it helps in avoiding the need to mention the base URL everywhere.

localhost:8081 is the endpoint where we volition exist edifice the Node/Express server later in this tutorial.

Now let'due south write a handler to upload the file when the class is submitted:

                          const              [              selectedFiles              ,              setSelectedFiles              ]              =              useState              ()              const              [              progress              ,              setProgress              ]              =              useState              ()              const              submitHandler              =              e              =>              {              e              .              preventDefault              ()              //prevent the course from submitting              let              formData              =              new              FormData              ()              formData              .              append              (              "              file              "              ,              selectedFiles              [              0              ])              axiosInstance              .              post              (              "              /upload_file              "              ,              formData              ,              {              headers              :              {              "              Content-Blazon              "              :              "              multipart/form-data              "              ,              },              onUploadProgress              :              data              =>              {              //Prepare the progress value to show the progress bar              setProgress              (              Math              .              round              ((              100              *              data              .              loaded              )              /              data              .              total              ))              },              })              }                      

Enter fullscreen fashion Exit fullscreen fashion

Here we are making use of ii local states, ane to concord the uploaded file details and another to concur the upload progress percent.
Also, brand sure that you lot are adding the content-type header every bit multipart/form-data, so that it works similar to normal course submit
and multer will exist able to parse the file in the back end.

Axios likewise accepts optional onUploadProgress holding, which is a callback with details about how much data is uploaded.

Now permit's bind the submit handler and the input field:

                          import              {              useState              }              from              "              react              "              import              {              Container              ,              Row              ,              Col              ,              Class              ,              Button              ,              ProgressBar              }              from              "              react-bootstrap              "              import              axiosInstance              from              "              ./utils/axios              "              function              App              ()              {              const              [              selectedFiles              ,              setSelectedFiles              ]              =              useState              ([])              const              [              progress              ,              setProgress              ]              =              useState              ()              const              submitHandler              =              east              =>              {              eastward              .              preventDefault              ()              //foreclose the form from submitting              allow              formData              =              new              FormData              ()              formData              .              append              (              "              file              "              ,              selectedFiles              [              0              ])              axiosInstance              .              postal service              (              "              /upload_file              "              ,              formData              ,              {              headers              :              {              "              Content-Blazon              "              :              "              multipart/form-data              "              ,              },              onUploadProgress              :              data              =>              {              //Set the progress value to show the progress bar              setProgress              (              Math              .              round              ((              100              *              information              .              loaded              )              /              data              .              full              ))              },              })              }              return              (              <              Container              >              <              Row              >              <              Col              lg              =              {              {              span              :              iv              ,              starting time              :              3              }              }              >              <              Grade              action              =              "http://localhost:8081/upload_file"              method              =              "post"              encType              =              "multipart/form-data"              onSubmit              =              {              submitHandler              }              >              <              Form              .              Group              >              <              Class              .              File              id              =              "exampleFormControlFile1"              label              =              "Select a File"              name              =              "file"              onChange              =              {              e              =>              {              setSelectedFiles              (              e              .              target              .              files              )              }              }              />              </              Course              .              Grouping              >              <              Form              .              Group              >              <              Button              variant              =              "info"              type              =              "submit"              >              Upload              </              Button              >              </              Form              .              Group              >              {              progress              &&              <              ProgressBar              now              =              {              progress              }              label              =              {              `              ${              progress              }              %`              }              />              }              </              Grade              >              </              Col              >              </              Row              >              </              Container              >              )              }              export              default              App                      

Enter fullscreen mode Leave fullscreen mode

Also, we are showing the progress bar whenever it has some value using the ProgressBar component from react-bootstrap.

Creating the backend Node Project

Now we accept the client-side ready, let'southward build the server-side. Inside the server folder run the following command to create a node project.

                          npm init              -y                      

Enter fullscreen mode Exit fullscreen fashion

Update the package.json that is created with the following beginning script:

                          {                                          "name"              :                                          "server"              ,                                          "version"              :                                          "1.0.0"              ,                                          "description"              :                                          ""              ,                                          "principal"              :                                          "alphabetize.js"              ,                                          "scripts"              :                                          {                                          "exam"              :                                          "echo                            \"              Error: no exam specified              \"                              && exit ane"              ,                                          "start"              :                                          "node index.js"                                          },                                          "keywords"              :                                          [],                                          "writer"              :                                          ""              ,                                          "license"              :                                          "ISC"                                          }                                                  

Enter fullscreen style Leave fullscreen manner

Now nosotros need to take the following modules added to our project:

  • express - Used to create a web framework with node.js
  • multer - A node.js middleware for handling multipart/form-data, which is primarily used for uploading files
  • cors - Enabling CORS policies for the client URL.

Run the following control to install the above packages in the server project:

            yarn add limited multer cors                      

Enter fullscreen mode Exit fullscreen mode

Now create a file named upload.js inside the server project with the following code:

                          const              multer              =              require              (              "              multer              "              )              const              storage              =              multer              .              diskStorage              ({              //Specify the destination directory where the file needs to be saved              destination              :              function              (              req              ,              file              ,              cb              )              {              cb              (              null              ,              "              ./uploads              "              )              },              //Specify the name of the file. The date is prefixed to avoid overwriting of files.              filename              :              function              (              req              ,              file              ,              cb              )              {              cb              (              null              ,              Date              .              at present              ()              +              "              _              "              +              file              .              originalname              )              },              })              const              upload              =              multer              ({              storage              :              storage              ,              })              module              .              exports              =              upload                      

Enter fullscreen mode Exit fullscreen way

Hither we are creating the multer case, by specifying the destination and the file proper name in which the uploaded file needs to be saved.

Now create a file named index.js with the following code:

                          const              express              =              crave              (              "              limited              "              )              const              upload              =              require              (              "              ./upload              "              )              const              multer              =              require              (              "              multer              "              )              const              cors              =              require              (              "              cors              "              )              const              app              =              limited              ()              //Add together the client URL to the CORS policy              const              whitelist              =              [              "              http://localhost:3000              "              ]              const              corsOptions              =              {              origin              :              part              (              origin              ,              callback              )              {              if              (              !              origin              ||              whitelist              .              indexOf              (              origin              )              !==              -              i              )              {              callback              (              null              ,              truthful              )              }              else              {              callback              (              new              Error              (              "              Non immune by CORS              "              ))              }              },              credentials              :              true              ,              }              app              .              use              (              cors              (              corsOptions              ))              app              .              post              (              "              /upload_file              "              ,              upload              .              single              (              "              file              "              ),              role              (              req              ,              res              )              {              if              (              !              req              .              file              )              {              //If the file is not uploaded, then throw custom error with bulletin: FILE_MISSING              throw              Mistake              (              "              FILE_MISSING              "              )              }              else              {              //If the file is uploaded, then send a success response.              res              .              send              ({              status              :              "              success              "              })              }              })              //Express Error Handling              app              .              use              (              function              (              err              ,              req              ,              res              ,              next              )              {              // Cheque if the mistake is thrown from multer              if              (              err              instanceof              multer              .              MulterError              )              {              res              .              statusCode              =              400              res              .              send              ({              code              :              err              .              code              })              }              else              if              (              err              )              {              // If it is not multer mistake then check if information technology is our custom error for FILE_MISSING              if              (              err              .              bulletin              ===              "              FILE_MISSING              "              )              {              res              .              statusCode              =              400              res              .              send              ({              lawmaking              :              "              FILE_MISSING              "              })              }              else              {              //For any other errors set code equally GENERIC_ERROR              res              .              statusCode              =              500              res              .              transport              ({              code              :              "              GENERIC_ERROR              "              })              }              }              })              //Kickoff the server in port 8081              const              server              =              app              .              heed              (              8081              ,              function              ()              {              const              port              =              server              .              address              ().              port              console              .              log              (              "              App started at http://localhost:%due south              "              ,              port              )              })                      

Enter fullscreen mode Exit fullscreen way

In the above code,

  • We have created a Mail route at /upload_file and call upload function exported from upload.js. The proper noun file passed inside the upload.single() function should match with that of FormData in the axios call written before.
  • We have added the CORS policy for out client URL. This code snippet tin can be reused in whatsoever limited projection which requires to handle CORS.
  • Multer will add the details of the file uploaded to req.file. So if req.file does not accept any information, that means the file is non uploaded. Multer by default does not throw whatsoever error if the file is missing. So we are throwing an express error with a bulletin FILE_MISSING
  • We take an error handler for limited which looks for both Multer errors and express errors and nosotros laissez passer the appropriate fault code in the response.

Before running the application, let's create the directory uploads where the uploaded files will exist saved.

Now if you run the awarding, using the command npm start in ii dissever terminals,
one within the client and another inside the server directory, you will see the progress bar in action:

Upload Animation

I have used a huge file (200MB) to upload, since uploading to localhost is pretty fast and we will not be able to see the progress bar correctly.
You lot tin can make use of the network throttling feature of the browser as well.

If you lot check the uploads directory now, you should be able to meet the file there:
Uploaded File

Error handling

At present let'south show appropriate error messages when the upload has failed.

When the file is not uploaded

If the user has failed to select a file before clicking upload, we need to inform the user.
For that, allow's update App.js with a catch concatenation for the axios phone call:

                          import              {              useState              }              from              "              react              "              import              {              Container              ,              Row              ,              Col              ,              Course              ,              Button              ,              ProgressBar              ,              Alert              ,              }              from              "              react-bootstrap              "              import              axiosInstance              from              "              ./utils/axios              "              part              App              ()              {              const              [              selectedFiles              ,              setSelectedFiles              ]              =              useState              ([])              const              [              progress              ,              setProgress              ]              =              useState              ()              const              [              error              ,              setError              ]              =              useState              ()              const              submitHandler              =              e              =>              {              e              .              preventDefault              ()              //prevent the form from submitting              permit              formData              =              new              FormData              ()              formData              .              suspend              (              "              file              "              ,              selectedFiles              [              0              ])              //Articulate the error message              setError              (              ""              )              axiosInstance              .              post              (              "              /upload_file              "              ,              formData              ,              {              headers              :              {              "              Content-Blazon              "              :              "              multipart/grade-data              "              ,              },              onUploadProgress              :              information              =>              {              //Set the progress value to show the progress bar              setProgress              (              Math              .              round              ((              100              *              information              .              loaded              )              /              data              .              full              ))              },              })              .              catch              (              error              =>              {              const              {              code              }              =              mistake              ?.              response              ?.              information              switch              (              code              )              {              instance              "              FILE_MISSING              "              :              setError              (              "              Please select a file before uploading!              "              )              intermission              default              :              setError              (              "              Deplorable! Something went wrong. Delight endeavor again later              "              )              break              }              })              }              return              (              <              Container              >              <              Row              >              <              Col              lg              =              {              {              bridge              :              4              ,              starting time              :              3              }              }              >              <              Class              action              =              "http://localhost:8081/upload_file"              method              =              "post"              encType              =              "multipart/grade-data"              onSubmit              =              {              submitHandler              }              >              <              Form              .              Group              >              <              Form              .              File              id              =              "exampleFormControlFile1"              characterization              =              "Select a File"              proper noun              =              "file"              onChange              =              {              east              =>              {              setSelectedFiles              (              due east              .              target              .              files              )              }              }              />              </              Grade              .              Group              >              <              Form              .              Group              >              <              Button              variant              =              "info"              type              =              "submit"              >              Upload              </              Button              >              </              Class              .              Group              >              {              error              &&              <              Alert              variant              =              "danger"              >              {              error              }              </              Alert              >              }              {              !              error              &&              progress              &&              (              <              ProgressBar              now              =              {              progress              }              label              =              {              `              ${              progress              }              %`              }              />              )              }              </              Grade              >              </              Col              >              </              Row              >              </              Container              >              )              }              export              default              App                      

Enter fullscreen mode Exit fullscreen way

In the above lawmaking, whenever an error occurs we are setting the error bulletin to the error state and displaying using the
Alarm component

File Missing Error

Preventing huge file uploads

When we need to restrict the size of the file uploaded, we can add that configuration in upload.js in the server project:

                          const              multer              =              require              (              "              multer              "              )              const              storage              =              multer              .              diskStorage              ({              //Specify the destination directory where the file needs to be saved              destination              :              function              (              req              ,              file              ,              cb              )              {              cb              (              cipher              ,              "              ./uploads              "              )              },              //Specify the proper noun of the file. The date is prefixed to avoid overwriting of files.              filename              :              function              (              req              ,              file              ,              cb              )              {              cb              (              zero              ,              Appointment              .              now              ()              +              "              _              "              +              file              .              originalname              )              },              })              const              upload              =              multer              ({              storage              :              storage              ,              limits              :              {              fileSize              :              1024              *              1024              ,              },              })              module              .              exports              =              upload                      

Enter fullscreen mode Exit fullscreen way

Now let'due south update our switch case in App.js in client side:

                          switch              (              code              )              {              instance              "              FILE_MISSING              "              :              setError              (              "              Please select a file earlier uploading!              "              )              break              example              "              LIMIT_FILE_SIZE              "              :              setError              (              "              File size is also large. Please upload files below 1MB!              "              )              break              default              :              setError              (              "              Sorry! Something went wrong. Please effort over again later              "              )              intermission              }                      

Enter fullscreen way Leave fullscreen mode

Now if you attempt to upload a file larger than ane MB, you lot should encounter the error message:

Large File

Restricting file types

When we demand to allow only certain type of files, nosotros tin can add a fileFilter to the multer configuration equally shown below:

                          const              upload              =              multer              ({              storage              :              storage              ,              limits              :              {              fileSize              :              1024              *              1024              ,              },              fileFilter              :              (              req              ,              file              ,              cb              )              =>              {              if              (              file              .              mimetype              ==              "              paradigm/png              "              ||              file              .              mimetype              ==              "              image/jpg              "              ||              file              .              mimetype              ==              "              prototype/jpeg              "              )              {              cb              (              null              ,              true              )              }              else              {              cb              (              naught              ,              faux              )              render              cb              (              new              Mistake              (              "              INVALID_TYPE              "              ))              }              },              })                      

Enter fullscreen mode Exit fullscreen way

Also, permit's tweak the error handler in index.js to adapt the new error code:

                          // ...              //Express Error Handling              app              .              use              (              function              (              err              ,              req              ,              res              ,              next              )              {              // Check if the error is thrown from multer              if              (              err              instanceof              multer              .              MulterError              )              {              res              .              statusCode              =              400              res              .              send              ({              code              :              err              .              lawmaking              })              }              else              if              (              err              )              {              // If information technology is not multer error then check if it is our custom error for FILE_MISSING & INVALID_TYPE              if              (              err              .              message              ===              "              FILE_MISSING              "              ||              err              .              bulletin              ===              "              INVALID_TYPE              "              )              {              res              .              statusCode              =              400              res              .              ship              ({              code              :              err              .              message              })              }              else              {              //For any other errors fix code as GENERIC_ERROR              res              .              statusCode              =              500              res              .              send              ({              code              :              "              GENERIC_ERROR              "              })              }              }              })              // ...                      

Enter fullscreen mode Exit fullscreen mode

Finally, add a new instance to the switch condition in App.js:

                          switch              (              code              )              {              case              "              FILE_MISSING              "              :              setError              (              "              Delight select a file earlier uploading!              "              )              break              case              "              LIMIT_FILE_SIZE              "              :              setError              (              "              File size is besides large. Please upload files beneath 1MB!              "              )              break              case              "              INVALID_TYPE              "              :              setError              (              "              This file type is non supported! Only .png, .jpg and .jpeg files are allowed              "              )              break              default              :              setError              (              "              Sorry! Something went wrong. Please attempt again later              "              )              break              }                      

Enter fullscreen mode Go out fullscreen mode

Now upload a file that is not an image and see if information technology shows the mistake:

Invalid Type

Source code

You can view the complete source code here.

colonthatimensfa.blogspot.com

Source: https://dev.to/collegewap/uploading-files-in-react-with-progress-bar-using-express-server-58cf

0 Response to "If You Want to See the Progress of the Upload"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel