{"id":29146050,"url":"https://github.com/thy3634/chunked-uploader","last_synced_at":"2025-09-11T12:34:30.769Z","repository":{"id":255063378,"uuid":"843850344","full_name":"Thy3634/chunked-uploader","owner":"Thy3634","description":"Split file into chunks and upload. resumable, retriable, offline aware. Works on node, browser, and workers.","archived":false,"fork":false,"pushed_at":"2024-09-27T13:00:39.000Z","size":27914,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-24T05:43:16.012Z","etag":null,"topics":["chunked-uploads","huge-file-upload","huge-files-uploader","uploader"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Thy3634.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-17T15:57:57.000Z","updated_at":"2024-10-26T03:55:12.000Z","dependencies_parsed_at":"2024-08-27T20:54:15.540Z","dependency_job_id":null,"html_url":"https://github.com/Thy3634/chunked-uploader","commit_stats":null,"previous_names":["thy3634/chunked-uploader"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Thy3634/chunked-uploader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thy3634%2Fchunked-uploader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thy3634%2Fchunked-uploader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thy3634%2Fchunked-uploader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thy3634%2Fchunked-uploader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thy3634","download_url":"https://codeload.github.com/Thy3634/chunked-uploader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thy3634%2Fchunked-uploader/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262850294,"owners_count":23374357,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chunked-uploads","huge-file-upload","huge-files-uploader","uploader"],"created_at":"2025-06-30T21:10:23.496Z","updated_at":"2025-06-30T21:10:24.437Z","avatar_url":"https://github.com/Thy3634.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# chunked-uploader\n\n\u003c!-- automd:badges color=yellow packagephobia --\u003e\n\n[![npm version](https://img.shields.io/npm/v/@thy3634/chunked-uploader?color=yellow)](https://npmjs.com/package/@thy3634/chunked-uploader)\n[![npm downloads](https://img.shields.io/npm/dm/@thy3634/chunked-uploader?color=yellow)](https://npmjs.com/package/@thy3634/chunked-uploader)\n\n\u003c!-- /automd --\u003e\n\nSplit file into chunks and upload. resumable, offline aware. Works on node, browser, and workers.\n\n## Demo\n\nA Master-Worker pattern to upload a file in chunks. A master worker to process file, split it into chunks and upload, Using several workers to calculate MD5 of chunk.\n\nInclude both backend and frontend.\n\n[![StackBlitz](https://img.shields.io/badge/StackBlitz-Open%20Demo-blue?logo=stackblitz)](https://stackblitz.com/~/github.com/Thy3634/chunked-uploader)\n\nOr git clone this repository and run `pnpm dev` to see the demo.\n\n## Usage\n\nInstall package:\n\n\u003c!-- automd:pm-install --\u003e\n\n```sh\n# ✨ Auto-detect\nnpx nypm install @thy3634/chunked-uploader\n\n# npm\nnpm install @thy3634/chunked-uploader\n\n# yarn\nyarn add @thy3634/chunked-uploader\n\n# pnpm\npnpm install @thy3634/chunked-uploader\n\n# bun\nbun install @thy3634/chunked-uploader\n```\n\n\u003c!-- /automd --\u003e\n\nImport:\n\n\u003c!-- automd:jsimport cjs cdn imports=\"ChunkedUploader\" --\u003e\n\n**ESM** (Node.js, Bun)\n\n```js\nimport { ChunkedUploader } from \"@thy3634/chunked-uploader\";\n```\n\n**CommonJS** (Legacy Node.js)\n\n```js\nconst { ChunkedUploader } = require(\"@thy3634/chunked-uploader\");\n```\n\u003e [conditional exports](https://nodejs.org/api/packages.html#packages_conditional_exports)\n\n**CDN** (Deno, Bun and Browsers)\n\n```js\nimport { ChunkedUploader } from \"https://esm.sh/@thy3634/chunked-uploader\";\n```\n\n\u003c!-- /automd --\u003e\n\n1. Create a new instance of `ChunkedUploader`:\n\n```js\n// usually you need to get a upload ID from server\nconst { id } = await ofetch('/api/chunked-upload', {\n    method: 'POST',\n    body: {\n        fileSize: file.size,\n        filename: file.name\n    }\n})\n// create a new instance of ChunkedUploader\nconst uploader = new ChunkedUploader(file, async ({ buffer, index, start, end, digest }) =\u003e ofetch(`/chunked-upload/${id}/${index}`, {\n                method: 'PUT',\n                body: await buffer,\n                headers: {\n                    'Range': `bytes=${start}-${end - 1}`,\n                    'Content-Digest': `md5=:${hexStringToBase64(await digest)}:`,\n                },\n                retry: 1,\n            }))\n```\n\n2. Listen to events:\n\n```js\nuploader.addEventListener('progress', ({ loaded, total }) =\u003e {\n    console.log(`progress: ${loaded}/${total}`)\n})\nuploader.addEventListener('error', (event) =\u003e {\n    console.error(uploader.error)\n})\nuploader.addEventListener('success', (event) =\u003e {\n    console.log('success')\n})\n```\n\n3. Start uploading:\n\n```js\nuploader.start()\n// or await for completion, which will return the response list, or throw error\nawait uploader.start()\n```\n\n## API\n\n### Class `ChunkedUploader`\n\n\u003e Extends [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)\n\n#### Constructor\n\n| Property | Type | Description |\n| --- | --- | --- |\n| file | [`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File) \\| `FileInfo \u0026 { chunks: Chunk[] }` | The file to upload, or an object containing the file's information and an array of chunks |\n| requester | `(chunk: Chunk) =\u003e Promise\u003cunknown\u003e` | A function that returns response based on each chunk and file information |\n| options | [`ChunkedUploaderOptions`](#ChunkedUploaderOptions) | Optional parameters to customize the uploader |\n\n#### Methods and Properties\n\n##### `start(): Promise\u003cunknown[]\u003e`\nStart the upload, if the upload is already started, do nothing, otherwise:\n- property `status` will be set to 'pending'\n- event `start` will be dispatched\n- if `onLine` is false, pause\n###### Parameters\n| Name | Type | Description |\n| --- | --- | --- |\n| skipIndexes | `number[] \\| undefined` | indexes of chunks to skip |\n\n##### `pause(): boolean`\nPause the upload, if it is not uploading, do nothing, otherwise:\n- property `status` will be set to 'paused'\n- method `abort` will be called\n- event `pause` will be dispatched\n\n##### `resume(): Promise\u003cunknown[]\u003e | false`\nResume the upload, if it is not paused, do nothing, otherwise:\n- property `status` will be set to 'pending'\n- event `resume` will be dispatched\n\n##### `store(): FileInfo \u0026 { chunks: Chunk[] }`\nGet the file information and chunks so that you can store them and reconstruct the uploader later.\n\n##### `status: 'idle' \\| 'pending' \\| 'paused' \\| 'error' \\| 'success'`\nThe current status of the upload process. read-only.\n\n##### `error: Error | undefined`\nThe error that occurred during the upload.\n\n##### `total: number`\nThe total number of chunks. read-only.\n\n##### `loaded: number`\nThe number of chunks that have been uploaded. read-only.\n\n##### `hash: string`\nA promise that resolves to the MD5 hash (hex) of the file's data. read-only.\n\n##### `chunks: Chunk[]`\nThe chunks array. read-only.\n\n##### `onLine: boolean`\nIs network online. If `navigator` is available, use the value of `navigator.onLine` as the default value, otherwise `true`.\n- When it is set to `false`, the upload will be paused. When it is set to `true`, the upload will be resumed. \n- If `window` is available, automatically update, and pause/resume the upload.\n\n##### `abort(): boolean`\nAbort the upload, if it is not uploading, do nothing, otherwise:\n- property `status` will be set to 'error'\n- event 'abort' will be dispatched\n- property `error` will be set to an `Error`\n\n#### Events\n\n- `start`: dispatched when the upload has started.\n- `progress`: dispatched periodically as any chunk uploaded.\n- `pause`: dispatched when the upload pauses.\n- `resume`: dispatched when the upload resumes.\n- `error`: dispatched when an error occurs during the upload.\n- `success`: dispatched when the upload completes successfully.\n- `end`: dispatched when the upload has completed, successfully or not.\n- `digestprogress`: Fired periodically as file digesting\n\n```js\n// you can listen to these events using the `on${EventType}` method:\nuploader.onpregress = (event) =\u003e {\n    console.log('progress', event.loaded / event.total)\n}\n// or using the `addEventListener` method:\nuploader.addEventListener('digestprogress', (event) =\u003e {\n    console.log('digestprogress', event.loaded / event.total)\n})\n```\n\n### Types\n\n#### `ChunkedUploaderOptions`\n\n| Property | Type | Description | Default |\n| --- | --- | --- | --- |\n| chunkSize | `number` | The size of each chunk in bytes. | 1024 * 1024 * 5 |\n| limit | `number` | The maximum number of concurrent requests. | Infinity |\n| createHasher | `() =\u003e Promise\u003cHasher\u003e \\| Hasher` | The hasher to use for calculating the hash of the file's data. | [hash-wasm](https://github.com/Daninet/hash-wasm)`createMD5` |\n| abortController | `AbortController` |  The controller to use for aborting the upload. | `new AbortController()` |\n\n\u003e Http headers suggestions:\n\u003e - [Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range)\n\u003e - [Content-Digest](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Digest)\n\n#### `Chunk`\n\n| Property | Type | Description |\n| --- | --- | --- |\n| index | `number` | The index of the chunk in the sequence of chunks that make up the file. |\n| start | `number` | An index into the file indicating the first byte to include in the buffer. |\n| end | `number` | An index into the file indicating the first byte that will not be included in the buffer (i.e. the byte exactly at this index is not included). |\n| buffer | [`ArrayBuffer \\| Promise\u003cArrayBuffer\u003e`](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBuffer) | The data. |\n| status | `'idle' \\| 'pending' \\| 'success'` | The status of the chunk |\n| response | `unknown` | The response of the chunk's upload request |\n| digest | `Promise\u003cstring\u003e \\| string` | A digest as a hexadecimal string |\n\n#### `FileInfo`\n\n| Property | Type | Description |\n| --- | --- | --- |\n| name | `string \\| undefined` | The name of the file |\n| size | `number` | The size of the file in bytes |\n\n#### `Hasher`\n\n| Property | Type | Description |\n| --- | --- | --- |\n| init | `(() =\u003e any) \\| undefined` | Initiate |\n| update | `(data: Uint8Array \\| Uint16Array \\| Uint32Array \\| string) =\u003e any` | Update the hasher with a chunk of data. |\n| digest | `() =\u003e Promise\u003cstring\u003e` | Get the hash as a hexadecimal string. |\n\n## Development\n\n\u003cdetails\u003e\n\n\u003csummary\u003elocal development\u003c/summary\u003e\n\n- Clone this repository\n- Install latest LTS version of [Node.js](https://nodejs.org/en/)\n- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`\n- Install dependencies using `pnpm install`\n- Run interactive tests using `pnpm dev`\n\n\u003c/details\u003e\n\n## License\n\n\u003c!-- automd:contributors license=MIT --\u003e\n\nPublished under the [MIT](https://github.com/Thy3634/chunked-uploader/blob/main/LICENSE) license.\nMade by [community](https://github.com/Thy3634/chunked-uploader/graphs/contributors) 💛\n\u003cbr\u003e\u003cbr\u003e\n\u003ca href=\"https://github.com/Thy3634/chunked-uploader/graphs/contributors\"\u003e\n\u003cimg src=\"https://contrib.rocks/image?repo=Thy3634/chunked-uploader\" /\u003e\n\u003c/a\u003e\n\n\u003c!-- /automd --\u003e\n\n\u003c!-- automd:with-automd --\u003e\n\n---\n\n_🤖 auto updated with [automd](https://automd.unjs.io)_\n\n\u003c!-- /automd --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthy3634%2Fchunked-uploader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthy3634%2Fchunked-uploader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthy3634%2Fchunked-uploader/lists"}