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

dextuploadx5-express-handler

v0.0.2

Published

Express upload handler for DEXTUploadX5 resumable and multipart uploads

Readme

DEXTUploadX5 Express Handler

Express upload handler for DEXTUploadX5 multipart and resumable uploads.

This package accepts regular multipart uploads and DEXTUploadX5 EXTS resume uploads, stores incoming chunks in a temporary directory, and exposes uploaded files as FileItem objects so your route can decide where and how to save them.

Features

  • Supports standard multipart uploads handled by DEXTUploadX5
  • Supports DEXTUploadX5 EXTS resumable uploads
  • Works as a normal Express route handler
  • Exposes uploaded files through a small FileItem API
  • Includes TypeScript declarations for TypeScript projects
  • Lets your application control final file names, directories, and response format
  • Keeps express and multer as peer dependencies

Requirements

  • Node.js 18 or later
  • Express 4.18+ or 5.x
  • Multer 2.1+

Installation

npm install dextuploadx5-express-handler express multer

Quick Start

import express from "express";
import { createDEXTUploadX5Handler } from "dextuploadx5-express-handler";

const app = express();

app.post(
    "/api/upload",
    createDEXTUploadX5Handler({
        uploadDir: process.env.DEXT_UPLOAD_DIR,
        tempDir: process.env.DEXT_TEMP_DIR,
        onComplete: async ({ res, file }) => {
            if (!file) {
                if (!res.headersSent) {
                    res.status(400).json({ success: false, message: "No file uploaded" });
                }
                return;
            }

            const saved = await file.save();

            if (!res.headersSent) {
                res.status(200).json({
                    success: true,
                    file: {
                        name: saved.name,
                        path: saved.savedPath,
                        size: saved.size,
                    },
                });
            }
        },
    })
);

How It Works

  1. The handler receives incoming files through multer.
  2. Uploaded files are written to tempDir.
  3. The handler converts each uploaded file into a FileItem.
  4. Your onComplete callback decides whether to persist the file by calling save().
  5. If you do not call save(), the temporary file is not preserved as a final upload.

For EXTS resumable uploads, the handler also manages upload session state and appends incoming chunks until the upload is complete.

Express Integration Example

import express from "express";
import { createDEXTUploadX5Handler } from "dextuploadx5-express-handler";

const router = express.Router();

router.post(
    "/upload",
    createDEXTUploadX5Handler({
        uploadDir: process.env.DEXT_UPLOAD_DIR,
        tempDir: process.env.DEXT_TEMP_DIR,
        onComplete: async ({ res, files }) => {
            const results = [];

            for (const item of files) {
                const saved = await item.save();
                results.push(saved.savedPath);
            }

            res.status(200).type("text/plain; charset=utf-8").send(results.join("\n"));
        },
    })
);

export default router;

EXTS Resumable Upload Example

When DEXTUploadX5 sends EXTS requests, the handler automatically interprets DEXT_EXTENSION_ACTION, restores the upload session, and provides the completed file to onComplete.

import express from "express";
import { createDEXTUploadX5Handler } from "dextuploadx5-express-handler";

const router = express.Router();

router.post(
    "/upload-exts",
    createDEXTUploadX5Handler({
        uploadDir: process.env.DEXT_UPLOAD_DIR,
        tempDir: process.env.DEXT_TEMP_DIR,
        secret: process.env.DEXT_UPLOAD_SECRET,
        onComplete: async ({ res, file }) => {
            if (!file) {
                res.status(400).type("text/plain; charset=utf-8").send("error=no_file");
                return;
            }

            const saved = await file.save();
            res.status(200).type("text/plain; charset=utf-8").send(saved.savedPath);
        },
    })
);

export default router;

POST Upload Response Example

If your DEXTUploadX5 integration expects a plain-text mapping response, you can format it directly in onComplete.

import express from "express";
import { createDEXTUploadX5Handler } from "dextuploadx5-express-handler";

const router = express.Router();

let fileCounter = 0;

router.post(
    "/upload-for-post",
    createDEXTUploadX5Handler({
        uploadDir: process.env.DEXT_UPLOAD_DIR,
        tempDir: process.env.DEXT_TEMP_DIR,
        onComplete: async ({ req, res, files }) => {
            const rawIds = req.body["DEXTUploadX5_UniqueId"];
            const ids = Array.isArray(rawIds) ? rawIds : [rawIds];
            const lines = [];

            for (let i = 0; i < files.length; i++) {
                await files[i].save();
                fileCounter += 1;
                lines.push(`${ids[i]}=F${String(fileCounter).padStart(4, "0")}`);
            }

            res.status(200).type("text/plain; charset=utf-8").send(lines.join("\n"));
        },
    })
);

export default router;

API

createDEXTUploadX5Handler(options?)

Creates an Express request handler.

app.post("/api/upload", createDEXTUploadX5Handler(options));

Options

uploadDir?: string

Final directory used by FileItem.save() when no target is given.

  • Default: path.join(process.cwd(), "files", "store")
  • Created automatically if it does not exist

tempDir?: string

Directory used for incoming multipart files and EXTS working files.

  • Default: path.join(uploadDir, ".tmp")
  • Created automatically if it does not exist

secret?: string

Secret used to encrypt EXTS session keys.

  • Recommended for production
  • If omitted, the handler falls back to a key derived from process.cwd()
  • If options.secret is empty, process.env.DEXT_UPLOAD_SECRET is used when available

multer?: { fileFilter?, limits?, preservePath? }

Subset of multer options passed to the internal uploader.

Supported fields:

  • fileFilter
  • limits
  • preservePath

Not supported:

  • storage
  • dest

Those are managed internally because this package always stores incoming files in tempDir first.

onComplete?: (ctx) => void | Promise<void>

Called after a standard multipart upload completes, or after an EXTS upload receives its final chunk.

If you want to preserve the uploaded file, call file.save() or files[i].save() inside this callback.

If you send no response in onComplete, the handler ends the request with an empty 200 OK plain-text response.

onComplete Context

The callback receives:

onComplete({ req, res, file, files, exts })

req

Express request object.

  • Multipart form fields are available through req.body
  • req.files is replaced with FileItem[]

res

Express response object. Use it when you need to control the response format directly.

file

The first uploaded file, or null when no file was uploaded.

files

Array of uploaded FileItem objects.

exts

Available only when an EXTS upload finishes.

{
    key,
    action,
    meta
}
  • key: current EXTS session key
  • action: parsed DEXT_EXTENSION_ACTION values
  • meta: current session metadata written by the handler

FileItem

FileItem represents a temporary uploaded file.

Properties

tmpPath: string

Temporary file path currently used by the handler.

metaPath?: string

EXTS metadata path when the upload came from a resumable session.

originalName: string

Sanitized original file name.

size: number

Uploaded file size in bytes.

await file.save(target?)

Moves the temporary file to its final location.

Without arguments, the file is saved to uploadDir/originalName. If the same file name already exists, the package creates a unique name such as name (1).ext.

Returned value:

{
    savedPath: "D:/uploads/example.bin",
    name: "example.bin",
    size: 1234
}

Save to the default upload directory

const saved = await file.save();

Save to a specific directory and filename

const saved = await file.save({
    dir: "D:/uploads/customer-a",
    filename: "renamed.bin",
});

Overwrite an existing file

const saved = await file.save({
    dir: "D:/uploads/customer-a",
    filename: "renamed.bin",
    overwrite: true,
});

Save to an absolute path directly

const saved = await file.save("D:/uploads/customer-a/renamed.bin");

save() is idempotent for the same FileItem. After the first successful move, later calls return the same result.

Response Behavior

This package does not impose a fixed business response format.

  • Standard multipart uploads: your onComplete callback should return the response your client expects
  • EXTS preparing and attaching requests: the handler automatically returns the protocol responses needed by DEXTUploadX5 until the upload is complete
  • If onComplete is omitted, the handler ends with an empty plain-text 200 OK

Notes

  • Uploaded file names are sanitized before saving
  • Temporary files are cleaned up when the handler decides the upload should not continue
  • For EXTS uploads, only one chunk file is accepted per attaching request
  • If an EXTS session cannot be restored from the incoming key, the handler starts a new session and returns a new key

History

0.0.2

  • Added TypeScript declaration support for TypeScript projects

0.0.1

  • Initial package release

License

This package is free to use, including commercial use. Modification for internal use is allowed. Redistribution of modified versions is prohibited. DEXTUploadX5 itself is a commercial product and is not free to use under the same terms as DEXTUploadX5 Express Handler.

See the LICENSE file for full terms.