{"id":18544272,"url":"https://github.com/tiyee/uploader","last_synced_at":"2025-07-19T18:08:55.524Z","repository":{"id":64304699,"uuid":"495072762","full_name":"tiyee/uploader","owner":"tiyee","description":"大文件上传前后端方案，支持秒传，分片传输，断点续传。支持h5和微信小程序","archived":false,"fork":false,"pushed_at":"2025-05-23T10:46:30.000Z","size":170,"stargazers_count":15,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-23T13:23:00.931Z","etag":null,"topics":["chunked-uploads","file-upload","miniprogram","upload"],"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/tiyee.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2022-05-22T13:47:16.000Z","updated_at":"2025-05-23T10:44:43.000Z","dependencies_parsed_at":"2024-06-20T09:23:50.183Z","dependency_job_id":"58838bc2-af6c-4ce5-9e02-5099872e0563","html_url":"https://github.com/tiyee/uploader","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/tiyee/uploader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiyee%2Fuploader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiyee%2Fuploader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiyee%2Fuploader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiyee%2Fuploader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tiyee","download_url":"https://codeload.github.com/tiyee/uploader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiyee%2Fuploader/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265982908,"owners_count":23859576,"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","file-upload","miniprogram","upload"],"created_at":"2024-11-06T20:15:51.355Z","updated_at":"2025-07-19T18:08:55.512Z","avatar_url":"https://github.com/tiyee.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- @format --\u003e\n\n# Uploader\n\n大文件上传插件，支持秒传，分片上传，断点续传。兼容小程序。\n\n采用泛型注入依赖的方式，兼容 web 和小程序，在编译期减少了无用代码\n\n# build\n\n在 dist 文件有已经编译好的文件，当然，你也可以自己编译\n\n先安装依赖\n\n\u003e yarn install\n\n编译支持 web 的文件\n\n\u003e yarn build // 或者 yarn dev\n\n编译支持小程序的文件(小程序只编译了 commonjs 格式，如果需要，自己修改`rollup.wxm.config.js`)\n\n\u003e yarn wxm\n\n## Install\n\n1.  html 页面直接引用 dist 文件，用法参考`demo.html`\n2.  es 环境可以直接引用`dist`文件夹的**web/index.esm.js**文件，也可以直接引用`src/uploader.tsx`\n\n## 使用方法(html)\n\n```html\n\u003cbody\u003e\n    \u003cdiv id=\"root\"\u003e\n        \u003cinput type=\"file\" id=\"upload\" onchange=\"upload(this.files)\" /\u003e\n    \u003c/div\u003e\n    \u003cscript src=\"/dist/index.js\"\u003e\u003c/script\u003e\n    \u003cscript\u003e\n        document.getElementById('upload').onchange = function (e) {\n            console.log(uploader)\n            const file = e.target.files[0]\n            const ctx = {\n                maxConcurrency: 5,\n                totalSize: file.size,\n                chunkSize: 1024 * 1024,\n                uploadUrl: '/2/upload',\n                mergeUrl: '/2/merge',\n                touchUrl: '/2/init',\n            }\n            const up = uploader(ctx, file)\n            up.on('progress', e =\u003e {\n                console.log('progess', e)\n            })\n            up.on('success', e =\u003e {\n                console.log('success', e)\n                alert(e.url)\n            })\n            up.on('complete', e =\u003e {\n                console.log('complete', e)\n            })\n            up.run()\n        }\n    \u003c/script\u003e\n\u003c/body\u003e\n```\n\n## 监听事件\n\n- `success`，上传成功时触发，e = {\"upload_id\":\"xxxxx\",\"status\":1,\"url\":\"\",\"chunks\":[]},其中 status 定义 0:待上传，1：上传中，2：已完成，3：已失败\n\n- `fail`，上传失败时触发，e :Error\n- `complete`，上传成功或失败时触发，返回值同 success 或 fail\n- `progess`，上传进度变化时触发，返回内容如下 e={ uploadedSize: 1286679, totalSize: 1286679 }\n\n## 服务端接口\n\n### touchUrl (post)\n\n初始化上传动作，返回 upload_id，会携带文件 md5 和 mime 信息，\n\n如果文件已经存在，则直接返回 status=2，url 不为空。\n\n如果之前上传过，status=1,chunks 会返回已经上传的 chunk 的 index 和 hash\n\n#### ur 参数\n\n| 属性       | 类型     | 说明              |\n| ---------- | -------- | ----------------- |\n| size       | `String` | 整个文件的 md5 值 |\n| chunk_size | `String` | 整个文件的大小    |\n| digest     | `String` | 整个文件的 md5 值 |\n\n#### body\n\n文件的前 200 个字节(为了获取文件的`mime`)\n\n#### 返回参数\n\n| 属性      | 类型                                | 说明                                      |\n| --------- | ----------------------------------- | ----------------------------------------- |\n| url       | `String`                            | 已上传时返回线上文件路径                  |\n| upload_id | `String`                            | 上传凭证，如果已完成，该字段为空字符串    |\n| chunks    | `Array\u003c{index:number,etag:string}\u003e` | 未完全上传时，返回已上传的分块序号        |\n| status    | `int`                               | 0:待上传，1：上传中，2：已完成，3：已失败 |\n\n### uploadUrl (post)\n\n分片传输\n\n#### ur 参数\n\n| 属性      | 类型     | 说明                |\n| --------- | -------- | ------------------- |\n| index     | `String` | 分片序号，从 0 开始 |\n| upload_id | `String` | 传输凭证            |\n| digest    | `String` | 分片内容的 md5 值   |\n\n#### body\n\n传输的分片内容\n\n### mergeUrl\n\n合并传输分片\n\n#### ur 参数\n\n| 属性      | 类型     | 说明              |\n| --------- | -------- | ----------------- |\n| upload_id | `String` | 传输凭证          |\n| digest    | `String` | 整个文件的 md5 值 |\n\n#### body\n\njson 格式，所有分片的编号和摘要，Array\u003c{index:number,etag:string}\u003e\n\n## 微信小程序\n\n用法与 h5 基本一致，参考 wxm 文件夹下的代码\n\n因为小程序不支持`Blob`和`URLSearchParams`,所以规避了 BLob，同时根据`URLSearchParams`的 set 和 toString 方法，仿了一个简单实现，然后通过泛型注入。\n\n同时小程序获取文件 size 也是异步的，所以在方法实例的时候有所不同，实现差别如下\n\n```typescript\n// web 实现\nconst uploader = (ctx: IContext, f: File) =\u003e {\n    return new Uploader\u003cWebFile, WebRequest, URLSearchParams\u003e(ctx, new WebFile(f), new WebRequest(), URLSearchParams)\n}\n\n// 小程序实现\n\nconst fsm = wx.getFileSystemManager()\nconst getSize = (filePath: string): Promise\u003cnumber\u003e =\u003e {\n    return new Promise(resolve =\u003e {\n        fsm.getFileInfo({\n            filePath,\n            success(res) {\n                resolve(res.size)\n            },\n            fail(e: WechatMiniprogram.GetFileInfoFailCallbackResult) {\n                throw Error(e.errMsg)\n            },\n        })\n    })\n}\nconst uploader = async (ctx: IContext, filePath: string) =\u003e {\n    return getSize(filePath).then(n =\u003e {\n        return new Uploader\u003cWxmFile, WxmRequest, URLSearchParams\u003e(\n            ctx,\n            new WxmFile(fsm, filePath, n),\n            new WxmRequest(),\n            URLSearchParams,\n        )\n    })\n}\n```\n\n所以体现在实例化的差别\n\n```javascript\n// web\n\n            const file = e.target.files[0]\n            const ctx = {\n                maxConcurrency: 5,\n                totalSize: file.size,\n                chunkSize: 1024 * 1024,\n                uploadUrl: '/2/upload',\n                mergeUrl: '/2/merge',\n                touchUrl: '/2/init',\n\n            }\n            const up = uploader(ctx, file)\n            up.on('progress', e =\u003e {\n                console.log('progess', e)\n            })\n            up.on('success', e =\u003e {\n                console.log('success', e)\n                alert(e.url)\n            })\n            up.run()\n\n\n//  小程序\n\n        const ctx = {\n          maxConcurrency: 5,\n          totalSize: 0,\n          chunkSize: 1024 * 1024,\n          uploadUrl: '/2/uploader/upload',\n          mergeUrl: '/2/uploader/merge',\n          touchUrl: '/2/uploader/init',\n          testChunks: false,\n          headers:{} //如何有需要可以设置请求头\n\n        }\n         uploader(ctx, filePath).then(ins=\u003e{\n          ins.on('progress', e =\u003e {\n                console.log('progess', e)\n            })\n          ins.on('success',(e)=\u003e{\n            wx.showModal({\n              title: '地址',\n              content: e.url,\n              success (res) {\n                if (res.confirm) {\n                  console.log('用户点击确定')\n                } else if (res.cancel) {\n                  console.log('用户点击取消')\n                }\n              }\n            })\n\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiyee%2Fuploader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftiyee%2Fuploader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiyee%2Fuploader/lists"}