upcloud
Version:
A powerful and user friendly npm package for smooth file management and uploads with Cloudinary that integrates Multer to process incoming file requests.
525 lines (436 loc) • 19.1 kB
Markdown
# upcloud
A powerful and user friendly npm package for smooth file management and uploads with Cloudinary that integrates Multer to process incoming file requests.
## Badges







## Key Features
- **Effortless Cloudinary Integration**: Easily upload and manage files on Cloudinary
- **Multer Middleware**: Seamlessly integrate with Express.js for handling `multipart/form-data` primarily for file uploads
- **Flexible Upload Options**: Supports single and multiple file uploads with options for specifying folders and resource types
- **File Deletion**: Conveniently delete files from Cloudinary using their public IDs
- **Comprehensive Error Handling**: Provides detailed error responses for robust application development
- **Multiple Upload Methods**: Supports uploading from local paths, buffers, and streams, including a dedicated function for large files
- **Dual Module Support**: Works seamlessly with both CommonJS (`require`) and ES Modules (`import`)
## Installation
You can install `upcloud` using npm or yarn:
```bash
npm install upcloud
# or
yarn add upcloud
```
Additionally, for Express.js integration, you'll need `express` and `dotenv` (for environment variables):
```bash
npm install express dotenv
```
## Usage
### 1. Configure Cloudinary
Before using any upload functions, you need to configure your Cloudinary credentials. It's recommended to use environment variables.
**Create a `.env` file in your project root:**
```dotenv
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
```
**CommonJS (`.js` file):**
```javascript
const { configureCloudinary } = require("upcloud");
const dotenv = require("dotenv");
dotenv.config();
configureCloudinary({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
```
**ES Modules (`.mjs` file):**
```javascript
import { configureCloudinary } from "upcloud";
import dotenv from "dotenv";
dotenv.config();
configureCloudinary({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
```
### 2. Express.js Integration with Multer
This demonstrates setting up an Express server to handle file uploads via HTTP POST requests.
**CommonJS (`server.js`):**
```javascript
const express = require("express");
const dotenv = require("dotenv");
const fs = require("fs/promises");
const {
configureCloudinary,
createMulterDiskMiddleware,
uploadFile,
uploadMultipleFiles,
} = require("upcloud");
dotenv.config();
configureCloudinary({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
const app = express();
const port = 3000;
const upload = createMulterDiskMiddleware({ dest: "./uploads" });
fs.mkdir("./uploads", { recursive: true }).catch(console.error);
// single file upload
app.post("/upload-single", upload.single("image"), async (req, res) => {
if (!req.file) return res.status(400).json({ message: "No file uploaded" });
try {
const result = await uploadFile(req.file.path, {
folder: "express-uploads",
});
await fs.unlink(req.file.path);
res.status(result.statusCode).json(result);
} catch (error) {
console.error("Upload error:", error);
res
.status(500)
.json({ message: "File upload failed", error: error.message });
}
});
// multiple files upload
app.post("/upload-multiple", upload.array("images", 5), async (req, res) => {
if (!req.files || req.files.length === 0)
return res.status(400).json({ message: "No files uploaded" });
const filePaths = req.files.map((file) => file.path);
try {
const results = await uploadMultipleFiles(filePaths, {
folder: "express-multiple-uploads",
});
await Promise.all(filePaths.map((p) => fs.unlink(p)));
res.status(200).json({
status: true,
statusCode: 200,
message: "Files uploaded successfully",
data: results,
});
} catch (error) {
console.error("Multiple upload error:", error);
res
.status(500)
.json({ message: "Multiple file upload failed", error: error.message });
}
});
app.listen(port, () =>
console.log(`Server running on http://localhost:${port}`)
);
```
**ES Modules (`server.mjs`):**
```javascript
import express from "express";
import dotenv from "dotenv";
import fs from "fs/promises";
import { fileURLToPath } from "url";
import path from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import {
configureCloudinary,
createMulterDiskMiddleware,
uploadFile,
uploadMultipleFiles,
} from "upcloud";
dotenv.config();
configureCloudinary({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
const app = express();
const port = 3000;
const upload = createMulterDiskMiddleware({ dest: "./uploads" });
fs.mkdir("./uploads", { recursive: true }).catch(console.error);
app.post("/upload-single", upload.single("image"), async (req, res) => {
if (!req.file) return res.status(400).json({ message: "No file uploaded" });
try {
const result = await uploadFile(req.file.path, {
folder: "express-uploads-esm",
});
await fs.unlink(req.file.path);
res.status(result.statusCode).json(result);
} catch (error) {
console.error("Upload error:", error);
res
.status(500)
.json({ message: "File upload failed", error: error.message });
}
});
app.post("/upload-multiple", upload.array("images", 5), async (req, res) => {
if (!req.files || req.files.length === 0)
return res.status(400).json({ message: "No files uploaded" });
const filePaths = req.files.map((file) => file.path);
try {
const results = await uploadMultipleFiles(filePaths, {
folder: "express-multiple-uploads-esm",
});
await Promise.all(filePaths.map((p) => fs.unlink(p)));
res.status(200).json({
status: true,
statusCode: 200,
message: "Files uploaded successfully",
data: results,
});
} catch (error) {
console.error("Multiple upload error:", error);
res
.status(500)
.json({ message: "Multiple file upload failed", error: error.message });
}
});
app.listen(port, () =>
console.log(`Server running on http://localhost:${port}`)
);
```
**To test the Express routes:**
1. Start the server: `node server.js` (for CommonJS) or `node server.mjs` (for ES Modules)
2. Use `curl` or a tool like Postman/Insomnia to send `multipart/form-data` requests
- **Single file:** `curl -X POST -F 'image=@./path/to/your/image.jpg' http://localhost:3000/upload-single`
- **Multiple files:** `curl -X POST -F 'images=@./path/to/doc1.pdf' -F 'images=@./path/to/doc2.pdf' http://localhost:3000/upload-multiple`
### 3. Direct File Operations
These examples show how to use the `upcloud` functions directly without an Express server.
**CommonJS (`app.js`):**
```javascript
const {
configureCloudinary,
uploadFile,
uploadMultipleFiles,
deleteFile,
} = require("upcloud");
const {
uploadFromPath,
uploadBuffer,
uploadStream,
uploadLarge,
cloudinary,
} = require("upcloud/cloudinaryClient");
const dotenv = require("dotenv");
const path = require("path");
const fs = require("fs");
dotenv.config();
configureCloudinary({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
async function runDirectExamples() {
const imagePath = path.join(__dirname, "test-image.jpg");
const docPaths = [
path.join(__dirname, "test-doc1.pdf"),
path.join(__dirname, "test-doc2.pdf"),
];
const videoPath = path.join(__dirname, "test-video.mp4");
let uploadedPublicId = null;
// Example: Upload Single File
try {
const response = await uploadFile(imagePath, { folder: "direct-commonjs" });
console.log("Upload successful:", response.data[0].secure_url);
uploadedPublicId = response.data[0].public_id;
} catch (error) {
console.error("Upload failed:", error.message);
}
// Example: Upload Multiple Files
try {
const results = await uploadMultipleFiles(docPaths, {
folder: "direct-commonjs-docs",
});
results.forEach((res) => console.log(res.secure_url));
} catch (error) {
console.error("Multiple uploads failed:", error.message);
}
// Example: Upload from Buffer
try {
const buffer = Buffer.from("This is a test text file from a buffer.");
const result = await uploadBuffer(buffer, {
resource_type: "raw",
public_id: "buffer-test-commonjs",
});
console.log("Buffer upload successful:", result.secure_url);
} catch (error) {
console.error("Buffer upload failed:", error.message);
}
// Example: Upload from Stream
try {
const readableStream = fs.createReadStream(videoPath);
const result = await uploadStream(readableStream, {
resource_type: "video",
folder: "stream-commonjs",
});
console.log("Stream upload successful:", result.secure_url);
} catch (error) {
console.error("Stream upload failed:", error.message);
}
// Example: Delete File
if (uploadedPublicId) {
try {
const result = await deleteFile(uploadedPublicId);
console.log("Deletion successful:", result);
} catch (error) {
console.error("Deletion failed:", error.message);
}
} else {
}
// Example: Using raw cloudinary instance
try {
const resources = await cloudinary.api.resources({
type: "upload",
max_results: 3,
});
console.log(
"First 3 uploaded resources:",
resources.resources.map((r) => r.public_id)
);
} catch (error) {
console.error("Failed to list resources:", error.message);
}
}
runDirectExamples();
```
**ES Modules (`app.mjs`):**
```javascript
import {
configureCloudinary,
uploadFile,
uploadMultipleFiles,
deleteFile,
} from "upcloud";
import {
uploadFromPath,
uploadBuffer,
uploadStream,
uploadLarge,
cloudinary,
} from "upcloud/cloudinaryClient";
import dotenv from "dotenv";
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config();
configureCloudinary({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
async function runDirectExamples() {
const imagePath = path.join(__dirname, "test-image.jpg");
const docPaths = [
path.join(__dirname, "test-doc1.pdf"),
path.join(__dirname, "test-doc2.pdf"),
];
const videoPath = path.join(__dirname, "test-video.mp4");
let uploadedPublicId = null;
try {
const response = await uploadFile(imagePath, { folder: "direct-esm" });
console.log("Upload successful:", response.data[0].secure_url);
uploadedPublicId = response.data[0].public_id;
} catch (error) {
console.error("Upload failed:", error.message);
}
try {
const results = await uploadMultipleFiles(docPaths, {
folder: "direct-esm-docs",
});
results.forEach((res) => console.log(res.secure_url));
} catch (error) {
console.error("Multiple uploads failed:", error.message);
}
try {
const buffer = Buffer.from("This is a test text file from a buffer (ESM).");
const result = await uploadBuffer(buffer, {
resource_type: "raw",
public_id: "buffer-test-esm",
});
console.log("Buffer upload successful:", result.secure_url);
} catch (error) {
console.error("Buffer upload failed:", error.message);
}
try {
const readableStream = fs.createReadStream(videoPath);
const result = await uploadStream(readableStream, {
resource_type: "video",
folder: "stream-esm",
});
console.log("Stream upload successful:", result.secure_url);
} catch (error) {
console.error("Stream upload failed:", error.message);
}
// Example: Delete File
if (uploadedPublicId) {
try {
const result = await deleteFile(uploadedPublicId);
console.log("Deletion successful:", result);
} catch (error) {
console.error("Deletion failed:", error.message);
}
} else {
}
// Example: Using raw cloudinary instance
try {
const resources = await cloudinary.api.resources({
type: "upload",
max_results: 3,
});
console.log(
"First 3 uploaded resources:",
resources.resources.map((r) => r.public_id)
);
} catch (error) {
console.error("Failed to list resources:", error.message);
}
}
runDirectExamples();
```
**To run these direct usage examples:**
- For CommonJS: `node app.js`
- For ES Modules: `node app.mjs`
## Available Functions and Options
| Function / Interface | Description | Parameters | Returns |
| :--------------------------- | :------------------------------------------------------------------------------------------------ | :-------------------------------------------------------- | :----------------------------------------------------------------------- |
| `configureCloudinary` | Configures the Cloudinary SDK with your credentials. | `config: CloudinaryConfig` | `void` |
| `createMulterDiskMiddleware` | Creates a Multer middleware instance configured for disk storage. | `options?: multer.Options` (Multer options) | `multer.Multer` instance |
| `uploadFile` | Uploads a single file from a local path to Cloudinary. | `filePath: string`, `options?: UploadApiOptions` | `{ status: boolean; statusCode: number; message: string; data: any[]; }` |
| `uploadMultipleFiles` | Uploads an array of files from local paths to Cloudinary. | `filePaths: string[]`, `options?: UploadApiOptions` | `Promise<UploadApiResponse[]>` |
| `deleteFile` | Deletes a file from Cloudinary using its public ID. | `publicId: string`, `options?: UploadApiOptions` | `Promise<UploadApiResponse>` |
| `uploadFromPath` | (Advanced) Uploads a file from a local path. Returns raw Cloudinary response. | `localPath: string`, `opts?: Record<string, any>` | `Promise<UploadResult>` |
| `uploadBuffer` | (Advanced) Uploads a file from a Buffer. Returns raw Cloudinary response. | `buffer: Buffer`, `opts?: Record<string, any>` | `Promise<UploadResult>` |
| `uploadStream` | (Advanced) Uploads a file from a Readable Stream. Returns raw Cloudinary response. | `readable: stream.Readable`, `opts?: Record<string, any>` | `Promise<UploadResult>` |
| `uploadLarge` | (Advanced) Uploads a very large file (>100MB) from a local path. Returns raw Cloudinary response. | `localPath: string`, `opts?: Record<string, any>` | `Promise<UploadResult>` |
| `cloudinary` | (Advanced) The raw configured Cloudinary SDK instance for direct API calls. | N/A | `cloudinary.v2` instance |
---
#### `CloudinaryConfig` Interface
| Property | Type | Description | Required |
| :----------- | :------- | :-------------------------- | :------- |
| `cloud_name` | `string` | Your Cloudinary cloud name. | Yes |
| `api_key` | `string` | Your Cloudinary API key. | Yes |
| `api_secret` | `string` | Your Cloudinary API secret. | Yes |
#### `UploadApiOptions` Interface
| Property | Type | Description | Required |
| :-------------- | :------------------------------------------------------- | :------------------------------------------------------------------------------------------------ | :------- |
| `folder` | `string` | The name of the folder in your Cloudinary account to upload the file to. | No |
| `resource_type` | `ResourceType` (`"image" \| "video" \| "raw" \| "auto"`) | The type of resource being uploaded. Defaults to `"image"`. Use `"auto"` for automatic detection. | No |
#### `UploadApiResponse` Interface
| Property | Type | Description |
| :------------ | :------- | :------------------------------------------- |
| `public_id` | `string` | The public ID of the uploaded asset. |
| `secure_url` | `string` | The secure URL of the uploaded asset. |
| `[k: string]` | `any` | Other properties returned by Cloudinary API. |
## Contribution
Contributions are welcome. If you have suggestions for improvements, bug fixes or new features please open an issue or submit a pull request.
1. Fork the repository
2. Create a new branch (`git checkout -b feature/your-feature`)
3. Make your changes
4. Commit your changes (`git commit -m 'Add new feature'`)
5. Push to the branch (`git push origin feature/your-feature`)
6. Open a pull request
## License
This project is licensed under the MIT License - see the [LICENSE](https://mit-license.org) file for details.