Multipart Uploader
files-sdk/multipart-uploaderFreeComponent
Multi-file queue uploader with bounded concurrency, live progress and cancel.
Install it
npx shadcn@latest add https://files-sdk.dev/r/multipart-uploader.jsonSource
1"use client";23import type { UseFilesResult } from "files-sdk/react";4import { FileIcon, Loader2Icon, UploadIcon, XIcon } from "lucide-react";5import { useCallback, useRef, useState } from "react";67import { Button } from "@/components/ui/button";8import { Progress } from "@/components/ui/progress";9import { cn } from "@/lib/utils";1011type QueueStatus = "pending" | "uploading" | "success" | "error";1213interface QueueItem {14 id: number;15 file: File;16 status: QueueStatus;17 /** 0–1. */18 progress: number;19 key?: string;20 error?: string;21}2223export interface MultipartUploaderProps {24 /** A `useFiles()` instance — uploads through it. */25 files: UseFilesResult;26 /** Key prefix (folder) for explicit keys, e.g. `"docs/"`. Empty = server mints keys. */27 prefix?: string;28 /** `accept` attribute for the file input. */29 accept?: string;30 /** Parallel uploads. Default 3. */31 concurrency?: number;32 onUploaded?: (entry: { key: string; name: string }) => void;33 className?: string;34}3536/**37 * Multi-file queue uploader. Files upload with bounded concurrency and live38 * per-file progress; large files are chunked into a multipart upload by the39 * gateway transparently. Cancel aborts everything in flight.40 */41export const MultipartUploader = ({42 files,43 prefix = "",44 accept,45 concurrency = 3,46 onUploaded,47 className,48}: MultipartUploaderProps) => {49 const idRef = useRef(0);50 const inputRef = useRef<HTMLInputElement>(null);51 const [queue, setQueue] = useState<QueueItem[]>([]);52 const [isUploading, setIsUploading] = useState(false);5354 const patch = useCallback((id: number, next: Partial<QueueItem>) => {55 setQueue((prev) =>56 prev.map((item) => (item.id === id ? { ...item, ...next } : item))57 );58 }, []);5960 const add = useCallback((list: FileList | null) => {61 if (!list?.length) {62 return;63 }64 const additions: QueueItem[] = [...list].map((file) => {65 const id = idRef.current;66 idRef.current += 1;67 return { file, id, progress: 0, status: "pending" };68 });69 setQueue((prev) => [...prev, ...additions]);70 }, []);7172 const start = useCallback(async () => {73 const pending = queue.filter((item) => item.status === "pending");74 if (!pending.length) {75 return;76 }77 // Re-arm the hook in case a previous run was cancelled (aborted) controller.78 files.reset();79 setIsUploading(true);8081 let cursor = 0;82 const worker = async () => {83 while (cursor < pending.length) {84// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from files-sdk
All 12 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Capabilities Badgescapabilities-badges | files-sdk | Components | Free | 2 deps | |
| Dropzonedropzone | files-sdk | Components | Free | 2 deps | |
| File Actionsfile-actions | files-sdk | Components | Free | 2 deps | |
| File Browserfile-browser | files-sdk | Components | Free | 2 deps | |
| File Listfile-list | files-sdk | Components | Free | 2 deps | |
| File Previewfile-preview | files-sdk | Components | Free | 2 deps | |
| File Searchfile-search | files-sdk | Components | Free | 2 deps | |
| Share Dialogshare-dialog | files-sdk | Components | Free | 2 deps |
