dextuploadx5-express-handler
v0.0.2
Published
Express upload handler for DEXTUploadX5 resumable and multipart uploads
Maintainers
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
FileItemAPI - Includes TypeScript declarations for TypeScript projects
- Lets your application control final file names, directories, and response format
- Keeps
expressandmulteras peer dependencies
Requirements
- Node.js 18 or later
- Express 4.18+ or 5.x
- Multer 2.1+
Installation
npm install dextuploadx5-express-handler express multerQuick 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
- The handler receives incoming files through
multer. - Uploaded files are written to
tempDir. - The handler converts each uploaded file into a
FileItem. - Your
onCompletecallback decides whether to persist the file by callingsave(). - 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.secretis empty,process.env.DEXT_UPLOAD_SECRETis used when available
multer?: { fileFilter?, limits?, preservePath? }
Subset of multer options passed to the internal uploader.
Supported fields:
fileFilterlimitspreservePath
Not supported:
storagedest
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.filesis replaced withFileItem[]
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 keyaction: parsedDEXT_EXTENSION_ACTIONvaluesmeta: 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
onCompletecallback 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
onCompleteis omitted, the handler ends with an empty plain-text200 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.
