npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@satyajeetsahoo/storage

v2.1.7

Published

Small local and adapter-based file storage package

Readme

@satyajeetsahoo/storage

A unified storage service supporting Local, Azure Blob, and Cloudflare R2 storage.

Why use this package?

Writing files to the local disk works great in development, but fails immediately when you deploy to serverless environments or multi-node clusters. This package lets you write code against a single storage interface. You can save files locally during development, and swap to Azure Blob or Cloudflare R2 in production without changing your file upload logic.

Features

  • Upload anywhere: One method saves to Local Disk, Azure, or Cloudflare.
  • Built-in Image Resizing: Resize images on the fly before saving them using sharp.

Installation

npm install @satyajeetsahoo/storage

Configuration & Strategies

Local Storage Strategy (Development)

import { StorageFactory, STORAGE_TYPES } from '@satyajeetsahoo/storage';

export const storage = StorageFactory.createStorage({ 
  type: STORAGE_TYPES.local, 
  rootDirectory: './uploads' 
});

Azure Blob Storage Strategy (Production)

import { StorageFactory, STORAGE_TYPES } from '@satyajeetsahoo/storage';

export const storage = StorageFactory.createStorage({ 
  type: STORAGE_TYPES.azure, 
  connectionString: process.env.AZURE_CONN, 
  containerName: 'uploads' 
});

Cloudflare Images/Stream Strategy (Production)

For generating direct upload URLs for videos and images.

import { StorageFactory, STORAGE_TYPES } from '@satyajeetsahoo/storage';

export const streamStorage = StorageFactory.createStorage({ 
  type: STORAGE_TYPES.cloudflare, 
  accountId: process.env.CF_ACCOUNT_ID, 
  apiToken: process.env.CF_API_TOKEN 
});

Cloudflare R2 Strategy (Production)

For saving standard files (documents, PDFs) using S3 protocols.

import { StorageFactory, STORAGE_TYPES } from '@satyajeetsahoo/storage';

export const r2Storage = StorageFactory.createStorage({ 
  type: STORAGE_TYPES.cloudflare, 
  r2: { 
    endpoint: process.env.R2_ENDPOINT,
    accessKey: process.env.R2_ACCESS_KEY,
    secretKey: process.env.R2_SECRET_KEY,
    bucket: process.env.R2_BUCKET
  }
});

Method Compatibility Matrix

Because storage providers are fundamentally different, not all methods are supported by all strategies. Using an unsupported method will throw a NotImplementedError.

| Method | Local / Azure | Cloudflare R2 | Cloudflare Images/Stream | |--------|---------------|---------------|--------------------------| | saveFile() | ✅ Yes | ❌ No | ❌ No | | deleteFile() | ✅ Yes | ❌ No | ❌ No | | resizeImage() | ✅ Yes | ❌ No | ❌ No | | getPresignedUploadUrl() | ❌ No | ✅ Yes | ❌ No | | getPresignedDownloadUrl()| ❌ No | ✅ Yes | ❌ No | | getImageUploadUrl() | ❌ No | ❌ No | ✅ Yes | | getVideoUploadUrl() | ❌ No | ❌ No | ✅ Yes | | getVideoStatus() | ❌ No | ❌ No | ✅ Yes | | deleteImage() | ❌ No | ❌ No | ✅ Yes | | deleteVideo() | ❌ No | ❌ No | ✅ Yes |

Usage Examples

Prerequisite Note for Express: In order for req.files to be populated with a data buffer (as seen in the examples below), you must install and use a file-parsing middleware in your Express app, such as express-fileupload or multer (with memory storage).

npm install express-fileupload
import fileUpload from 'express-fileupload';
app.use(fileUpload());

1. Uploading a File

const savedFilePath = await storage.saveFile(
  { data: req.files.avatar.data }, // The raw Buffer
  `users/${req.user.id}`,           // The target folder
  req.files.avatar.name            // The target filename
);

console.log(savedFilePath); // e.g., 'users/123/avatar.jpg'

2. Resizing an Image

await storage.resizeImage(
  `users/${req.user.id}/avatar.jpg`, 
  `users/${req.user.id}/avatar_thumbnail.jpg`
);

3. Deleting a File

await storage.deleteFile(`users/${req.user.id}/avatar.jpg`);

4. Cloudflare R2: Generate Presigned Upload URL

If you want to allow clients to upload directly to R2 without routing the file through your Node.js server, generate a presigned upload URL:

const result = await storage.getPresignedUploadUrl({
  prefix: 'user_uploads',
  id: req.user.id,
  fileType: 'IMAGE',
  contentType: 'image/jpeg',
  fileExtension: 'jpg'
});

// Send this back to the client so they can PUT the file directly to R2
console.log(result.uploadURL);
console.log(result.key); // The R2 path where the file will be saved

5. Cloudflare R2: Generate Presigned Download URL

Generate a secure, expiring URL to let clients download private files.

const downloadUrl = await storage.getPresignedDownloadUrl(
  'user_uploads/123/2026-09-04/abc.jpg',
  'image/jpeg',
  true // Forces a download attachment in the browser
);

console.log(downloadUrl);

6. Cloudflare Images: Direct Upload URL

Generate a one-time upload URL for Cloudflare Images. This allows clients to upload images directly to Cloudflare without hitting your server.

const imageUpload = await storage.getImageUploadUrl();

console.log(imageUpload.uploadURL); // Send to client to PUT the image
console.log(imageUpload.id);        // Store this ID in your database

7. Cloudflare Stream: Direct Video Upload URL

Generate a one-time upload URL for Cloudflare Stream.

const videoUpload = await storage.getVideoUploadUrl({
  maxDurationSeconds: 3600 // Optional: Restrict video length
});

console.log(videoUpload.uploadURL); // Send to client
console.log(videoUpload.uid);       // Store this UID in your database

8. Cloudflare Stream: Get Video Status

Check if a video has finished processing and is ready to stream.

const status = await storage.getVideoStatus('video_uid_from_db');

console.log(status.readyToStream); // boolean
console.log(status.playback);      // HLS/DASH playback URLs
console.log(status.thumbnail);     // URL to the video thumbnail

9. Cloudflare Images: Delete an Image

await storage.deleteImage('image_id_from_db');

10. Cloudflare Stream: Delete a Video

await storage.deleteVideo('video_uid_from_db');

© 2026 Satyajeet Sahoo. All Rights Reserved.