Today we’ll explore how to create an Express.js server that handles image uploads and downloads utilizing Zoho’s Catalyst SDK. This tutorial aims to guide you through setting up a straightforward application and, along the way, teach some best practices for file handling.
Prerequisites:
- Basic knowledge of Express.js
- Zoho Catalyst account
- Familiarity with asynchronous programming in JavaScript
Initial Setup:
- Create a new Express.js project.
- Install the required dependencies:
npm install express zcatalyst-sdk-node uuid
Structure:
We are mainly focusing on two routes:
/notes/imageupload
: To handle the image uploads./notes/downloadImage/:id
: To facilitate image downloads.
Setting up the Middleware:
Before diving into the main logic, we need to ensure we have the required middleware to handle file uploads. This can be achieved using express-fileupload
or any other middleware of your choice.
Core Logic:
We begin by initializing some constants and importing required modules:
const catalyst = require("zcatalyst-sdk-node");
const uuidv4 = require("uuid").v4;
const path = require("path");
const FOLDER_ID = "9417000002445170";
Note: The folder ID is specific to the Zoho Catalyst environment where files are stored.
For security and validation purposes, define allowed MIME types and file extensions:
const allowedMimeTypes = ["image/jpeg", "image/png", "image/gif", "image/bmp", "image/webp"];
const allowedExtensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"];
Image Upload Route:
The upload endpoint begins by initializing the Catalyst app instance and extracting the uploaded file data.
const app = catalyst.initialize(req);
const uploadedFile = req.files.data;
Several validation steps are performed:
- Ensure the MIME type is one of the allowed types.
- Validate the file extension.
- Confirm that the uploaded file size does not exceed 5MB.
Upon validation, the file is temporarily saved using a unique filename.
const newFileName = `${uuidv4()}${path.extname(uploadedFile.name)}`;
await uploadedFile.mv(`/tmp/${uploadedFile.name}`);
Subsequently, we utilize the Catalyst SDK to store the file:
await app.filestore().folder(FOLDER_ID).uploadFile({
code: fs.createReadStream(`/tmp/${uploadedFile.name}`),
name: newFileName,
});
If everything goes smoothly, a response containing the filename, file ID, and URL to download the file is sent back.
Image Download Route:
For downloading, it’s quite straightforward. We extract the file ID from the URL parameters and use the Catalyst SDK:
const fileObject = await app.filestore().folder(FOLDER_ID).downloadFile(req.params.id);
The file is then sent back as a response to the client with proper headers.
Full Code:
const express = require('express');
const catalyst = require('zcatalyst-sdk-node');
const fileUpload = require('express-fileupload');
const uuidv4 = require('uuid').v4;
const path = require('path');
const app = express();
// Constants
const PORT = 3000;
const FOLDER_ID = "9417000002445170";
const allowedMimeTypes = ["image/jpeg", "image/png", "image/gif", "image/bmp", "image/webp"];
const allowedExtensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"];
// Middleware
app.use(fileUpload());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post("/notes/imageupload", async (req, res) => {
try {
const catalystApp = catalyst.initialize(req);
const uploadedFile = req.files.data;
const fileMimeType = uploadedFile.mimetype;
const fileExtension = path.extname(uploadedFile.name).toLowerCase();
// Validation steps
if (!allowedMimeTypes.includes(fileMimeType) || !allowedExtensions.includes(fileExtension)) {
return res.status(400).send({ status: "error", message: "Invalid file type or extension. Please upload an allowed image type." });
}
if (uploadedFile.size > 5 * 1024 * 1024) {
return res.status(400).send({ status: "error", message: "File size too large. Max allowed is 5MB." });
}
const newFileName = `${uuidv4()}${fileExtension}`;
await uploadedFile.mv(`/tmp/${newFileName}`);
await catalystApp.filestore().folder(FOLDER_ID).uploadFile({
code: fs.createReadStream(`/tmp/${newFileName}`),
name: newFileName,
}).then((fileObject) => {
res.status(200).json({
fileName: fileObject.file_name,
fileID: fileObject.id,
url: `/notes/downloadImage/${fileObject.id}`,
});
});
} catch (error) {
res.status(500).send({ status: "Internal Server Error", message: error.toString() });
}
});
app.get("/notes/downloadImage/:id", async (req, res) => {
try {
const catalystApp = catalyst.initialize(req);
const fileObject = await catalystApp.filestore().folder(FOLDER_ID).downloadFile(req.params.id);
res.writeHead(200, {
"Content-Length": fileObject.length,
"Content-Disposition": `inline; filename="${req.query.fileName}"`,
});
res.end(fileObject);
} catch (error) {
res.status(500).send({ status: "Internal Server Error", message: error.toString() });
}
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Conclusion:
Incorporating Zoho’s Catalyst SDK with Express.js streamlines the process of handling file uploads and downloads. Remember to always include validation checks and ensure the security of your application. Happy coding!