Helpers
Helpers for progress, validation, and file naming.
Import from @dimah-s3/react or @dimah-s3/core.
Upload UX helpers
Format upload progress
Returns human-readable progress like "1.2 MB / 5.6 MB (21%)".
import { formatUploadProgress } from "@dimah-s3/react";
formatUploadProgress(1_200_000, 5_600_000, 21);
// "1.2 MB / 5.6 MB (21%)"
formatUploadProgress(1_200_000, 0, 0);
// "1.2 MB"Format speed
Bytes per second to text like "1.2 MB/s".
import { formatSpeed } from "@dimah-s3/react";
formatSpeed(1_200_000); // "1.2 MB/s"
formatSpeed(512); // "512 B/s"Format ETA
Remaining time from bytes-left and speed. Returns null when not calculable.
import { formatEta } from "@dimah-s3/react";
formatEta(4_400_000, 450_000); // "9s"
formatEta(90_000_000, 1_500_000); // "1m"
formatEta(5_400_000_000, 500_000); // "3h"
formatEta(0, 450_000); // nullCreate speed tracker
Sliding-window speed tracker in bytes/second.
Use it inside onProgress and call reset() whenever an upload restarts.
import { createSpeedTracker, formatEta, formatSpeed } from "@dimah-s3/react";
const tracker = createSpeedTracker(); // default 3s window
onProgress: ({ loaded, total }) => {
const speed = tracker.update(loaded);
formatSpeed(speed); // "450 KB/s"
formatEta(total - loaded, speed); // "9s"
};Format accept labels
Normalizes HTML accept entries to short display labels. Returns labels only — no sentences — so you can build localized copy in your UI.
import { formatAcceptLabels } from "@dimah-s3/react";
formatAcceptLabels(["image/*", ".pdf", ".txt"]);
// ["Images", "PDF", "TXT"]
formatAcceptLabels([
".png",
".gif",
".webp",
".pdf",
".docx",
".txt",
".csv",
"image/*",
"video/mp4",
"audio/mpeg",
]);
// ["PNG", "GIF", "WEBP", "PDF", "DOCX", "TXT", "CSV", "Images", "MP4", "MPEG"]| Input | Label |
|---|---|
.png | PNG |
.jpg / .jpeg | JPEG (both extensions map to one label) |
image/* | Images |
video/mp4 | MP4 |
In a component, call formatAcceptLabels directly and build your own localized sentence:
import { formatAcceptLabels } from "@dimah-s3/react";
function MyDropzone({ accept }: { accept?: string[] }) {
const labels = formatAcceptLabels(accept);
// ["Images", "PDF", "TXT"]
return <p>Accepted {labels.join(", ")}.</p>;
}UploadDropzone from @dimah-s3/ui does the same and adds English hint copy in the UI layer.
Validation and display helpers
Validate file
Checks accept and maxFileSize. Returns an error message or null.
Mirror these checks in upload.presignGuard. Client validation is not a
security boundary.
import { validateFile } from "@dimah-s3/core";
validateFile(file, { accept: ["image/*"] });
// null | 'File type ".pdf" is not allowed'
validateFile(file, { accept: [".png", ".jpg"] });
// null | 'File type ".gif" is not allowed'
validateFile(file, { maxFileSize: 10 * 1024 * 1024 });
// null | "File size exceeds 10.0 MB limit"Format file size
import { formatFileSize } from "@dimah-s3/core";
formatFileSize(0); // "0 B"
formatFileSize(1_500); // "1.5 KB"
formatFileSize(1_500_000); // "1.4 MB"Truncate filename
Shortens long names while preserving extension. Default max is 26 chars.
import { truncateFileName } from "@dimah-s3/core";
truncateFileName("very-long-document-name.pdf");
// "very-long-documen… .pdf"
truncateFileName("short.pdf");
// "short.pdf"Sanitize filename
Replaces ", \, and newlines for safer query params.
createS3Client applies this automatically when you pass fileName for
download requests.
import { sanitizeFileName } from "@dimah-s3/core";
sanitizeFileName('file"name.pdf'); // file_name.pdf
sanitizeFileName("report\r\n.pdf"); // report__.pdfParse file name
Extracts the original filename from Content-Disposition.
import { parseFileName } from "@dimah-s3/core";
parseFileName(res.headers.get("content-disposition"));
// "report.pdf" | undefined
parseFileName("attachment; filename*=UTF-8''%E6%8A%A5%E5%91%8A.pdf");
// "报告.pdf"