File Uploads with Multer
Relevant source files
The following files were used as context for generating this wiki page:
The e-commerce application utilizes the multer middleware for Node.js to handle multipart/form-data, which is primarily used for uploading files. This system is integral for features such as creating products with images, updating user avatars, and sending images in chat messages. Multer processes incoming files, saves them to the server's local disk, and makes file information available in the request object for controllers to process.
The implementation involves a central Multer configuration, specific controller endpoints that use the middleware, and frontend components that prepare and send the file data. Uploaded files are stored in the backend/uploads/ directory and served statically by the Express application.
Multer Configuration
The core configuration for Multer is defined in backend/multer.js. This file sets up storage, file filtering, and upload limits.
Sources: backend/multer.js
Storage Engine
The system uses multer.diskStorage to save files directly to the server's file system.
- Destination: The
destinationfunction specifies that all uploaded files should be saved in thebackend/uploads/directory.
Sources: backend/multer.js:6-8 - Filename: The
filenamefunction generates a unique name for each file to prevent naming conflicts. It combines the original filename (without extension), a timestamp, a random number, and forces the.pngextension.
Sources: backend/multer.js:9-13
File Filtering and Limits
To ensure security and manage resources, specific rules are applied to incoming files.
- File Filter: The
fileFilterfunction restricts uploads to image files only. It checks both the file extension and the MIME type against a list of allowed types (jpeg,jpg,png,gif,webp). If a file does not match, an error is thrown.
Sources: backend/multer.js:16-25 - Limits: The exported
uploadobject defines constraints on the uploads:fileSize: A maximum size of 5MB per file.files: A maximum of 10 files per request.
Sources: backend/multer.js:28-32
The following diagram illustrates the logic defined in the Multer configuration file.
This diagram shows the process an uploaded file goes through within the Multer configuration, from initial filtering to being saved on the disk with a unique name.
Sources: backend/multer.js:5-34
Configuration Summary
| Option | Value | Description |
|---|---|---|
storage | multer.diskStorage | Configures files to be saved on the local disk. |
destination | ./uploads | The directory where files are stored. |
filename | filename + "-" + uniqueSuffix + ".png" | Creates a unique filename using the original name, a timestamp, and a random number. |
fileFilter | `/jpeg | jpg |
limits.fileSize | 5 * 1024 * 1024 (5MB) | Sets the maximum allowed size for a single file. |
limits.files | 10 | Sets the maximum number of files allowed in a single multipart/form-data request. |
Sources: backend/multer.js:5-34
Server-Side Implementation
Multer is used as middleware in various Express routes to handle file uploads for different features.
Product Image Uploads
When a seller creates or updates a product, they can upload multiple images.
- Endpoint:
POST /api/v2/product/create-product - Middleware:
upload.array('images')
This middleware processes an array of files uploaded with the field name images. The controller then retrieves the filenames from req.files, creates an array of image URLs (filenames), and saves this array to the Product model in the database.
Sources: backend/controller/product.js:12-13, backend/controller/product.js:20-22
The sequence diagram below shows the flow for creating a new product with images.
This diagram illustrates the sequence of events when a client uploads product images, from the initial request to the final database entry.
Sources: backend/controller/product.js:11-32, frontend/src/components/Shop/CreateProduct.jsx:68-79
A similar process occurs for updating a product via the PUT /api/v2/product/update-product endpoint.
Sources: backend/controller/product.js:33-66
User Avatar Upload
Users can update their profile picture.
- Endpoint:
PUT /api/v2/user/update-avatar - Middleware:
upload.single('image')
This middleware handles a single file from a field named image. The controller logic finds the user, updates the avatar field with the new filename, and saves the user model. It also includes logic to delete the old avatar file from the server to save space.
Sources: backend/controller/user.js:101-102
Chat Image Upload
Users can send images in real-time chat conversations.
- Endpoint:
POST /api/v2/message/create-new-message - Middleware:
upload.single('images')
This endpoint uses upload.single('images') to process one image file from a field named images. The controller then saves the filename to the Messages model.
Sources: backend/controller/message.js:10-11, backend/controller/message.js:16-17
Static File Serving
After files are uploaded to the backend/uploads/ directory, they need to be publicly accessible. The main Express application file, app.js, configures a static file server for this purpose.
// backend/app.js:41
app.use("/", express.static(path.join(__dirname, "./uploads")));
This line tells Express to serve the files from the uploads directory at the root of the server's URL. For example, a file saved as my-image-123.png can be accessed at http://<backend-url>/my-image-123.png. This is how the frontend is able to display the uploaded product images and avatars.
Sources: backend/app.js:41
Client-Side Implementation
On the frontend, React components handle file selection and prepare the data for submission. The CreateProduct component is a prime example.
Sources: frontend/src/components/Shop/CreateProduct.jsx
State Management and File Handling
The component uses useState to store an array of selected image files.
// frontend/src/components/Shop/CreateProduct.jsx:13
const [images, setImages] = useState([]);
// frontend/src/components/Shop/CreateProduct.jsx:32-35
const handleImageChange = (e) => {
e.preventDefault();
let files = Array.from(e.target.files);
setImages((prevImages) => [...prevImages, ...files]);
};
The handleImageChange function is triggered by the file input. It converts the FileList object into an array and appends the new files to the existing images state.
Sources: frontend/src/components/Shop/CreateProduct.jsx:13, frontend/src/components/Shop/CreateProduct.jsx:32-36
Form Submission with FormData
To send files to the backend, the handleSubmit function constructs a FormData object. This is essential for multipart/form-data requests.
// frontend/src/components/Shop/CreateProduct.jsx:52-55
const newForm = new FormData();
images.forEach((image) => {
newForm.append('images', image);
});
Each image file is appended to the FormData object with the key 'images'. This key must match the field name expected by the Multer middleware on the backend (upload.array('images')). The FormData object, containing both text fields and files, is then sent with the POST request.
Sources: frontend/src/components/Shop/CreateProduct.jsx:52-55
The following flowchart shows the client-side process for handling product image uploads.
This diagram outlines the steps on the frontend, from a user selecting files to the final submission of the FormData object to the server.
Sources: frontend/src/components/Shop/CreateProduct.jsx:32-79
Conclusion
The file upload system, powered by Multer, is a robust and essential feature of the application. It provides a secure and structured mechanism for handling user-generated images for products, avatars, and messages. The combination of a centralized Multer configuration, dedicated API endpoints, and FormData on the client-side creates a complete and effective workflow for managing file uploads within the e-commerce platform.