{"id":19560047,"url":"https://github.com/plotdb/uploadr","last_synced_at":"2026-05-13T22:32:19.106Z","repository":{"id":40289827,"uuid":"238377192","full_name":"plotdb/uploadr","owner":"plotdb","description":null,"archived":false,"fork":false,"pushed_at":"2025-10-27T08:16:45.000Z","size":5600,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-27T10:24:36.517Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"LiveScript","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/plotdb.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-02-05T05:43:52.000Z","updated_at":"2025-10-27T08:16:49.000Z","dependencies_parsed_at":"2025-03-31T18:23:24.424Z","dependency_job_id":"3314874f-1f7c-464f-8915-66d3fa45afe9","html_url":"https://github.com/plotdb/uploadr","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/plotdb/uploadr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fuploadr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fuploadr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fuploadr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fuploadr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plotdb","download_url":"https://codeload.github.com/plotdb/uploadr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plotdb%2Fuploadr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33002657,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"ssl_error","status_checked_at":"2026-05-13T13:14:51.610Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-11T05:05:43.225Z","updated_at":"2026-05-13T22:32:19.100Z","avatar_url":"https://github.com/plotdb.png","language":"LiveScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @plotdb/uploadr\n\nA file upload library for modern web apps.\n\n - [Client Side](#client-side): Upload widget, file list viewer ( with a Pug template ) and provider adapters.\n - [Server side](#server-side): API endpoint for file storage with Express\n\n\n## Installation\n\nInstall @plotdb/uploadr via npm, along with its dependencies:\n\n    npm install --save @plotdb/uploadr @loadingio/ldquery\n\n\nand following dependencies for widgets, optionally only if you use builtin widgets:\n\n    npm install --save @loadingio/paginate @loadingio/debounce.js ldview ldloader proxise\n\n\n## Client Side\n\nIn a browser context, we need 2 parts:\n\n - uploader for a specific provider\n - widgets for both file uploading and file choosing\n\nBefore using, you need to include required files; for using without widgets, include main lib and @loadingio/ldquery:\n\n\n    \u003cscript src=\"path/to/@loadingio/ldquery/index.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"path/to/@plotdb/uploadr/index.min.js\"\u003e\u003c/script\u003e\n\n\nand dependencies if you want to use with widgets:\n\n    \u003clink rel=\"stylesheet\" type=\"text/css\" href=\"@plotdb/uploadr/uploadr.css\"/\u003e\n    \u003cscript src=\"path/to/@loadingio/ldquery/index.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"path/to/@loadingio/paginate/index.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"path/to/@loadingio/debounce.js/index.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"path/to/proxise/index.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"path/to/ldview/index.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"path/to/@plotdb/uploadr/index.min.js\"\u003e\u003c/script\u003e\n\n\nadditionally, include a specific provider. For example, `native` provider:\n\n    \u003cscript src=\"@plotdb/uploadr/providers/native/index.min.js\"\u003e\u003c/script\u003e\n\nFor more information about provider, check the [Provider section](#providers) below.\n\n\n### Usage, without widgets\n\nUpload with a specific provider is simple. For example, upload with the native provider:\n\n    uploadr.ext.native({\n      files: [{file: blobToUpload}, ...]\n      progress: function ({percent, val, len, item}) { ... }\n      opt: {route: \"path-to-your-native-provider-api-endpoint\"}\n    })\n\nWhile @plotdb/uploadr provides upload widgets, in this case no UI is involved. If this is all what you need and don't need uploadr widgets, you can skip to the [Providers section](#providers) below. Check `no-ui/` page under demo site for a working example of uploading without widgets.\n\n\n### Usage, with widgets\n\nUploadr widget provides a headless controller based on ldview so you can design your own widgets, yet a pair of Pug mixin as a predefined HTML template within a pug file are also available for you to use. Additionally, `@plotdb/block` modules corresponding to viewer and uploader are also available.\n\nTo upload files via the uploadr viewer, create an `uploadr.uploader` object through its constructor:\n\n    var up = new uploadr.uploader({ ... })\n\nwith the following options:\n\n - `root`: root element ( or selector ) of the upload widget.\n    - To customize widget, see [Widget Customization section](#widget-customization) below.\n - `provider`: object for provider information\n   - For detailed usage, see [Providers section](#providers) below.\n   - if omitted, falls back to `{config: {route: '/api/uploadr'}, host: 'native'}`\n - `accept`: comma-separated string of file extensions, such as `jpg,jpeg,png` or `pdf`\n - `i18n`: a i18next compatible i18n object.\n   - It should at least supports `t()` and `addResourceBundle()` with namespace support.\n\n\nFor root element - if you use Pug, you can use the `uploadr-uploader` mixin available in `uploadr.pug` to create the DOM needed:\n\n    include \u003cpath-to-uploadr.pug\u003e\n    div.some-tag-to-wrap-uploader: +uploadr-uploader(\"scope-name\")\n\nOr, create a `uploadr.viewer` object to browse and choose files:\n\n    var up = new uploadr.viewer({ ... })\n\nwith the following options:\n\n - `root`: root element ( or selector ) of the upload widget.\n    - To customize widget, see [Widget Customization section](#widget-customization) below.\n - `page`: an object for configuration for fetching new content.\n   - this object wil be passed to `@loadingio/paginate`. See `@loadingio/paginate` for documentation.\n   - items in returned list from fetch should contain at least a member `url` for showing the URL of the image.\n - `i18n`: see `uploader` option.\n\nSimilar to `uploadr.uploader`, a mixin `uploadr-viewer` is available by including `uploadr.pug`:\n\n    include \u003cpath-to-uploadr.pug\u003e\n    div.some-tag-to-wrap-uploader-viewer: +uploadr-viewer(\"scope-name\")\n\n\nFeel free to wrap uploader or viewer in dialogs or popups. See demo site for more examples.\n\n\n### Usage, with @plotdb/block\n\nLoad uploader or viewer widget by following:\n\n    manager = new block.manager registry: -\u003e /* your registry definition */\n    \u003c- manager.from({name: \"@plotdb/uploadr\"}, {root: document.body, data: ...}).then _\n    \u003c- manager.from({name: \"@plotdb/uploadr\", path: \"viewer/index.html\"}, {root: document.body, data: ...}).then _\n\nWhere data is the constructor option of corresponding widget, except for the `root` and `i18n` fields (these are managed by `@plotdb/block` mechanism).\n\n\nYou can also use them along with ldcvmgr:\n\n    mgr = new ldcvmgr({...})\n    \u003c- mgr.get({name: \"@plotdb/uploadr\"}, {root: document.body, data: ...}).then _\n\n\n### API\n\n`uploadr.uploader` object provides following APIs:\n\n  - `init` - initialize uploadr, return a promise that resolves when initialized.\n    - constructor initializes uploadr automatically.\n    - simply use `init.then( ... )` to ensure initialized.\n  - `upload` - upload chosen files.\n  - `clear` - clear chosen files.\n  - `get` - get chosen files.\n  - `on(name, cb)` - listen to `name` event with `cb` callback. Following events are available:\n    - `preview:loading`\n    - `preview:loaded`\n    - `file:chosen`\n    - `upload:done`\n    - `upload:fail`\n  - `i18n(lng)`: translate text used in widget.\n    - parameter lng is optional. default language of the translation module will be used if omitted.\n    - only available if i18n object is provided during initialization.\n\n`uploader.viewer` object provides following APIs:\n\n  - `fetch` - force fetching new content.\n  - `reset` - reset viewer content\n  - `on(name, cb)` - listen to `name` event with `cb` callback. Following events are available:\n    - `file:chosen`: fired when any item is chosen.\n      - `cb` called with a list of object for chosen files with following fields:\n        - `url`: file url\n        - `size`: file size\n        - `name`: file name\n        - `lastModified`: file last modified time\n    - `fetch:fetched`: when fetching new items. list of items passed as parameter.\n    - `fetch:end`:  when there is no new item available.\n    - `fetch:empty`:  when list is empty.\n  - `i18n(lng)`: translate text used in widget. parameter lng is optional.\n    - only available if i18n object is provided during initialization.\n\n\n### Providers\n\n`@plotdb/uploadr` supports uploading to different kinds of file hosting services. use `provider` to choose from the available providers below.\n\nTo use a provider, you should make sure to\n\n - client side: initialize `uploadr` with proper provider configurations\n - server side: ensure to add API endpoint if needed.\n\nTo upload without UI ( Uploadr Viewer ), use the client-side providers directly, available via:\n\n    uploadr.ext[\"\u003cprovider-name\u003e\"]\n\nClient side providers are functions accepting an object with the following fields:\n\n - `files`: Array of files to upload. Items for each file are objects with the following fields:\n   - `thumb`: thumbnail URL\n   - `file`: corresponding file object\n - `progress(opt)`: progress event handler accepting an opt object with the following fields:\n   - `percent`: progress. 0 ~ 1\n   - `item`: uploading item object with the same structure as described in `files`.\n - `opt`: corresponding configs described in sections of each provider below.\n - `data`: additional data passed via the `data` field ( accessible via `req.fields.data` on the server side )\n\nFor example, to upload a file to Google Cloud Storage:\n\n    uploader.ext.gcs({\n      files: [{file: new File([\"hello\"], \"hello.txt\", {type: \"plain/text\"})],\n      progress: function(opt) { console.log(opt.percent); },\n      opt: {bucket: \"my-gcs-bucket\"}\n    });\n\nIt always return a Promise resolved with a list of object with fields listed in the `Other Providers` section, check it for more information.\n\n\nProvider configurations are described below.\n\n\n#### Native\n\nUpload files to a local API endpoint. include `providers/native/index.min.js` then:\n\n    new uploadr({provider: { host: \"native\", config: { ... }}});\n\nwhere the config contains:\n\n - `route`: API endpoint\n\n\n#### ImgBB\n\nUpload images to ImgBB. Include `providers/imgbb/index.min.js` then:\n\n    new uploadr({provider: { host: \"imgbb\", config: { ... }}});\n\nwhere the config contains:\n\n - `key`: imgbb API key for uploading images.\n\n\n#### GCS ( Google Cloud Storage )\n\nUpload files to Google Cloud Storage directly from browser. Include `providers/gcs/index.min.js` then initialize with:\n\n    new uploadr({provider: {host: \"gcs\", config: { ... }}});\n\nwhere the config contains:\n\n - `bucket`: bucket name in your google cloud storage to hold your files.\n - `domain`: domain name to access your files (including schema ).\n   - if omitted, falls back to `https://storage.googleapis.com`\n   - this is for previewing / downloading files.\n - `route`: server route to request signed URL for uploading files.\n   - if omitted, falls back to `/d/uploadr/gcs`.\n\n\n#### Dummy\n\nDummy provider doesn't upload files anywhere - it just responds with a dummy result. Include `providers/dummy/index.min.js` then:\n\n    new uploadr({provider: { host: \"dummy\" }})\n\nand there is no config for dummy provider.\n\n\n#### Other providers\n\nYou can also add provider for services you'd like to use by simply adding a function in `uploadr.ext`:\n\n    uploadr.ext.myService = function ({files, progress, opt}) { ... }\n\n\nIt's your job to implement the upload mechanism with the following parameters and requirements:\n\n - Parameters\n   - `files`: Array of `{thumb, file}` object with:\n     - `thumb`: thumbnail link ( blob URL )\n     - `file`: file object ( blob ) from input element to upload.\n   - `progress({percent, val, len, item})`: function to be called when progress is reported, with options:\n     - `percent`: percent of size uploaded\n     - `val`: actual bytes uploaded\n     - `len`: file size\n     - `item`: object in `files` array that is making progress.\n   - `opt`: the provider config object.\n   - `data`: additional data to pass to server.\n     - packed by `FormData` and accessible through `req.fields.data` as string when using `express-formidable`.\n     - need to manually parse to JSON in server, if a JSON object is passed from client.\n - A provider function should always return a Promise that resolves a list of objects when upload is finished.\n   - resolved object in list should contain the following members:\n     - `id`: unique ID for this file.\n     - `name`: name of this file. falls back to ID if omitted.\n     - `url`: URL for previewing this file.\n     - `download-url`: URL for downloading this file. falls back to `url` if omitted.\n     - `size`: file size. optional\n     - `err`: information if uploading of this file failed.\n\n\n### Widget Customization\n\nUploadr client library uses [ldview](https://github.com/loadingio/ldview) for UI abstraction. If you design your own upload widget, simply add the following `ld` names on corresponding elements.\n\nHere is for uploader:\n\n - `drop`: area for dropping files to choose them.\n - `file`: `ld-each` type name. preview of chosen files. with the following nested `ld` names:\n   - `thumb`: element for showing preview image. should also be an `\u003cimg\u003e` tag.\n   - `progress`: upload progress indicator\n   - `name`: name of the chosen file.\n   - `size`: size of the chosen file.\n   - `modifiedtime`: modified time of the chosen file.\n   - `delete`: file is un-chosen when element with `delete` name is clicked.\n - `input`: `input` element with `type='file'` attribute. For manually uploading with file picker dialog.\n - `upload`: upload chosen files to server when clicked.\n - `clear`: clear all files when clicked.\n - `loader`: a `running` class will be added to element(s) with this name.\n\nand here is for viewer:\n\n - `file`: same with uploadr, along with the selector under `file`.\n - `load`: a DOM element triggering additional load when clicking. Hidden when no more content to load.\n - `end`: a DOM element to show when there's no more content to load.\n - `reset`: a DOM element to trigger list reset. Also, only show when there's no more content to load.\n - `loader`: a `running` class will be added to element(s) with this name.\n\n\n\n## Server Side\n\nTo save files locally ( or after authenticated ), you will need a server-side API. Depending on how you store uploaded files, the implementation will vary. These implementations are separated into different modules called `provider`.\n\n\n### Common Usage\n\n    up = uploadr.provider {host: 'native', config: { .. /* provider specific config */ .. } }\n    app.post \\path, express-formidable({multiples: true}), up.getUploadRouter!\n    # note: not all providers implemented getDownloadRouter for now.\n    app.get \\path/:id, express-formidable({multiples: true}), up.getDownloadRouter!\n\nConfiguration:\n\n - `host`: provider name, such as `native`, `gcs`.\n - `config`: provider configuration. check doc for each provider below for more information.\n\nAdditionally:\n - `express-formidable({multiples: true})` is for passing form data. it depends on how you will use `uploadr` and can be tweaked accordingly.\n - `uploadr.provider` always provides following two APIs:\n   - `getUploadRouter`: its interface depends on provider's implementation. For example:\n     - provider `native` reads and stores files listed in `req.files`\n     - provider `gcs` reads optional `req.body.name` field.\n   - `getDownloadRouter`: always read `req.params.id` for identifying files to download.\n\n\n### adopt\n\n`adopt` is available as a config in all providers. `adopt` function is used to track a file, and by default do nothing, left to users to implement. it should be an object providing the following two member functions:\n\n - `upload(req, ret)` - called when we expect a file storing slot is created.\n - `download(req, ret)` - called before file is served to user.\n\nwhere the return value should be an object with the following fields:\n\n - `name`: filename\n - `id`: file ID, provided by provider.\n - `url`: optional. available for native provider.\n\nboth function should return a promise. `adopt` can be used to\n\n - return a rejected promise to prevent the file from being uploaded or downloaded\n - track uploads / downloads\n\nThe following is an example of using `adopt` in native provider:\n\n    up = uploader.provider({\n      host: 'native', config: {\n        adopt: {\n          upload: function() { ... },\n          download: function() { ... }\n        }\n      }\n    })\n\n\n### Native Provider\n\nNative provider accepts incoming request with files payload, and save them using a hashed ID into a specified location. Use `express-formidable` and `uploadr(...).route` to handle files:\n\n    up = uploader.provider {host: 'native', config: { ... }}\n    app.post \\/d/uploadr, express-formidable({multiples: true}), up.getUploadRouter!\n\nExample configurations:\n\n    {folder: 'static/assets/files', url: '/assets/files'}\n\n\n#### Configurations\n\nConfigure the native provider with the following options:\n\n - `config`: the native provider specific configs, including:\n   - `folder`: fs path for saving all files. if omitted, fall back to `uploads`\n   - `url`: URL prefix ( relative or absolute ). if omitted, falls back to `folder`\n - `adopt: (req, {name, path, url, id})`: post process function after files are saved.\n   - if provided, will be called for each file saved.\n   - options:\n     - `req`: express request object\n     - `name`: name of the file\n     - `path`: optional. `path` for the file in file system\n     - `url`: optional. `url` for accessing this file\n     - `id`: `id` for the file\n - `catch: (err, req, res, next)`: Promise rejection handler.\n   - if omitted, falls back to `res.status(505).send()` when exception occurs.\n - `log`: log function. if omitted, falls back to `console.log`.\n\n\n#### APIs\n\nFollowing are the APIs exposed by native provider:\n\n - `handler(req, res, next)`: process req.files and return a promise that resolves with an array of `{url, id ,name}`.\n - `router(req, res, next)`: wrap handler as a route that passes data to res.send, or report 500 on error.\n - `archive(opt)`: function that takes care of files\n   - input: one of following ( name is optional in both case )\n     - {path, name}\n     - {buf, name}\n   - return:\n     - { name, url, id } if successful\n     - { name } otherwise\n\n\n### GCS Provider\n\nThe GCS provider ( for Google Cloud Storage ) doesn't store files on the local server so there is no file passed to server. Instead, a URL is returned to the client for uploading / downloading files to an assigned bucket in Google cloud storage.\n\nBasic usage is similar to native provider:\n\n    up = uploader.provider {host: 'gcs', config: { ... }}\n    app.post \\/d/uploadr, express-formidable({multiples: true}), up.getUploadRouter!\n\n\n#### Configurations\n\n - `config`: GCS config including following fields:\n   - `projectId`: project ID. e.g., `sample-id`\n   - `keyFilename`: path to your private key file for accessing specific project. e.g., `sample-prk.json`\n   - `bucket`: bucket name. e.g., `sample-bucket`\n   - `limit`: maximal amount of files in one shot. default 10 if omitted.\n\n#### CORS Note\n\nBefore you can upload file via browser directly to Google Cloud Storage, you have to set CORS policy with gsutil:\n\n    gsutil cors set cors.json gs://\u003cyour-bucket-name\u003e\n\nSample content of `cors.json`:\n\n    [{\n      \"maxAgeSeconds\": 3600,\n      \"method\": [\"GET\", \"HEAD\", \"PUT\"],\n      \"origin\": [\"http://localhost:3005\"],\n      \"responseHeader\": [\"Content-Type\", \"Access-Control-Allow-Origin\"]\n    }]\n\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplotdb%2Fuploadr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplotdb%2Fuploadr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplotdb%2Fuploadr/lists"}