
后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载文件上传是 Web 应用中最高频的需求之一。本文以 Midway Hooks 的官方文档 hooks/upload.md 为核心骨架讲解如何在 Midway Hooks 的纯函数pure function 一体化项目架构下通过midwayjs/hooks-upload配合midwayjs/upload组件快速实现可复用的文件上传接口。读完本文你将掌握组件安装与启用、Upload(path)接口声明、useFiles()/useFields()取值、前后端集成调用与手动 FormData 调用以及上传模式、白名单、MIME 校验、临时文件清理等配置与安全要点。一、方案概览为什么需要两个包Midway Hooks 提供了midwayjs/hooks-upload并与midwayjs/upload协作完成文件上传功能midwayjs/upload通用上传组件负责底层multipart/form-data请求的解析、扩展名白名单校验、MIME 类型校验、临时文件落盘与清理等能力同时兼容midwayjs/koa、midwayjs/faas、midwayjs/web、midwayjs/express等多套框架详见 extensions/upload.md。midwayjs/hooks-upload面向 Hooks 的封装层提供Upload(path)装饰器与useFiles()、useFields()等 Hooks 风格 API让文件上传接口与普通函数接口一样声明式、可调用。两者配合后前端可以直接像调用普通函数一样调用上传接口也可以使用原生FormData手动上传后端则始终以纯函数的方式编写逻辑。二、安装依赖在项目根目录执行npm install midwayjs/upload midwayjs/hooks-upload其中midwayjs/upload当前仓库内的版本为4.2.5见 packages/upload/package.json其对 Node.js 的引擎要求为20运行时依赖file-type与raw-body两个 npm 包。三、启用上传组件在后端目录的configuration.ts中启用midwayjs/upload组件。在imports数组中加入upload即可import { createConfiguration, hooks } from midwayjs/hooks; import * as Koa from midwayjs/koa; import * as upload from midwayjs/upload; /** * setup midway server */ export default createConfiguration({ imports: [ Koa, hooks(), upload, ], importConfigs: [{ default: { keys: session_keys } }], });组件被导入后其内部的UploadConfiguration会读取upload命名空间的配置并在onReady阶段自动把UploadMiddleware挂载到 koa、faas、express、egg 等应用实例上见 packages/upload/src/configuration.ts。也就是说一旦启用所有命中条件的multipart/form-data请求都会自动进入上传解析流程。四、创建上传接口在后端目录新建一个接口文件使用Api()包裹Upload(/api/upload)声明上传路由再在函数体内通过useFiles()获取上传文件import { Api } from midwayjs/hooks; import { Upload, useFiles, } from midwayjs/hooks-upload; export default Api( Upload(/api/upload), async () { const files useFiles(); return files; } );这里的关键点是Upload(path?: string)装饰器它把该接口声明为上传接口可指定路由路径默认POST并且只支持multipart/form-data类型的请求。函数体内部没有ctx、没有req文件数据以 Hooks 的方式直接注入这正是 Midway Hooks纯函数风格的体现。五、前端调用5.1 集成调用推荐在一体化项目中前端可以像调用普通函数一样直接import这个接口并传入files字段。以下是一个 React 表单示例import upload from ./api/upload; function Form() { const [file, setFile] React.useStateFileList(null); const handleSubmit async ( e: React.FormEventHTMLFormElement ) { e.preventDefault(); const files { images: file }; const response await upload({ files, }); console.log(response); }; const handleOnChange ( e: React.ChangeEventHTMLInputElement ) { console.log(e.target.files); setFile(e.target.files); }; return ( form onSubmit{handleSubmit} h1Hooks File Upload/h1 input multiple typefile onChange{handleOnChange} / button typesubmit Upload /button /form ); }注意传入的files是一个对象key 就是表单字段名例如上面的imagesvalue 是FileList。该 key 在后端会原样成为useFiles()返回对象的 key。5.2 手动调用FormData 上传如果不想借助 Hooks 客户端也可以直接使用浏览器原生FormDatafetch请求上传接口const input document.getElementById(file); const formdata new FormData(); formdata.append(file, input.files[0]); fetch(/api/upload, { method: POST, body: formdata, }) .then((res) res.json()) .then((res) console.log(res));由于接口是通过Upload(/api/upload)声明的/api/upload接受POST multipart/form-datafetch时只要把FormData作为 body 即可无需手动设置Content-Type浏览器会自动附带boundary。六、Hooks API 详解6.1Upload(path?: string)声明上传接口。path可指定路由路径默认生成POST接口且仅接受multipart/form-data类型请求。6.2useFiles()在函数体内调用用于获取上传的文件。返回值是Objectkey 为上传时的字段名field name当多个文件使用相同的字段名时value 为数组Array。// frontend await upload({ pdf }); // backend const files useFiles(); { pdf: { filename: test.pdf, // file original name Data: /var/tmp/xxx.pdf, // temporary file address of the server when mode is file fieldname: test1, // form field name mimeType: application/pdf, // mime } }从底层数据结构看单个文件对象的完整字段在 packages/upload/src/interface.ts 中定义为UploadFileInfoTfilename文件原始名、fieldName表单字段名、mimeTypeMIME 类型、datamode为file时是服务器临时文件地址字符串mode为stream时是Readable流。同时解析后的文件对象上还会被挂上一个 Symbol 类型的扩展名标记EXT_KEY见 packages/upload/src/constants.ts供后续落盘命名使用。6.3useFields()返回FormData中非文件部分的字段普通表单字段// frontend const formdata new FormData(); formdata.append(name, test); post(formdata); // backend const fields useFields(); // { name: test}如果启用了allowFieldsDuplication重名字段会被合并为数组例如{ name: [name1, name2] }。七、底层实现请求如何被解析了解底层流程有助于排查问题和合理配置。核心逻辑在 packages/upload/src/middleware.ts 的UploadMiddleware.execUpload中大致流程如下判定是否为上传请求getUploadBoundary()检查请求method是否为POST/PUT/DELETE/PATCH且content-type以multipart/form-data;开头并携带boundary。不满足则直接next()放行不影响普通接口。流式解析当请求体是ReadableStream且mode stream时走parseFromReadableStream流式拆包见 packages/upload/src/parse.ts逐 chunk 寻找 boundary避免大文件整体读入内存。整包解析mode file时先通过raw-body按fileSize限长读取 body再由parseMultipart按\r\n--boundary拆块解析Content-Disposition头中的filename与name区分文件与普通字段。扩展名校验checkAndGetExt()把文件名转小写后逐级截取扩展名与白名单比对不匹配则抛出MultipartInvalidFilenameError400 响应见 packages/upload/src/error.ts。MIME 校验若配置了mimeTypeWhiteList会通过file-type包读取文件二进制头识别真实 MIME与规则比对不通过则抛出MultipartInvalidFileTypeError。落盘或流转mode file时写入临时目录mode stream时包装成Readable。随后把fields与files挂到ctx上供useFiles()/useFields()消费。组件还向ctx注入了cleanupRequestFiles()方法用于主动删除当前请求产生的临时文件见 packages/upload/src/middleware.ts。八、配置项详解midwayjs/upload的所有配置均挂在upload命名空间下可在后端config.default.ts中覆盖。默认值定义在 packages/upload/src/config/config.default.ts类型定义见 packages/upload/src/interface.ts配置项默认值说明modefile上传模式file落盘到服务器临时目录或stream流式fileSize10mb最大上传文件大小字节/字符串格式whitelistuploadWhiteList允许上传的文件扩展名白名单设为null则不校验扩展名tmpdirjoin(tmpdir(), midway-upload-files)上传文件的服务器临时存储目录cleanTimeout5 * 60 * 1000临时文件自动清理间隔毫秒默认 5 分钟设为0可关闭自动清理base64false请求体是否为 base64 编码用于腾讯云 apigw 等场景兼容allowFieldsDuplicationfalse是否允许同名表单字段开启后合并为数组match/ignore无路径匹配规则match优先级高于ignoremimeTypeWhiteList无默认不校验扩展名到 MIME 的映射规则用于内容级校验一个完整的配置示例// src/config/config.default.ts import { uploadWhiteList } from midwayjs/upload; import { tmpdir } from os; import { join } from path; export default { // ... upload: { // mode: file 表示上传到服务器临时目录也可配置为 stream mode: file, // 最大上传文件大小默认 10mb fileSize: 10mb, // 扩展名白名单这里示例移除 .pdf whitelist: uploadWhiteList.filter(ext ext ! .pdf), // 上传文件的临时存储路径 tmpdir: join(tmpdir(), midway-upload-files), // 临时文件自动删除时间默认 5 分钟 cleanTimeout: 5 * 60 * 1000, // 原始 body 是否为 base64默认 false一般用于兼容腾讯云 base64: false, // 仅当路径命中 /api/upload 时才解析文件信息 match: /\/api\/upload/, }, };默认扩展名白名单uploadWhiteList可从midwayjs/upload包导出包含.jpg、.jpeg、.png、.gif、.bmp、.wbmp、.webp、.tif、.tiff、.psd、.svg、.js、.jsx、.json、.css、.less、.html、.htm、.xml、.pdf、.zip、.gz、.tgz、.gzip、.mp3、.mp4、.avi完整列表见 packages/upload/src/constants.ts。九、两种上传模式file 与 stream9.1 file 模式默认推荐data为上传文件在服务器上的临时文件地址之后可用fs.createReadStream等方法读取内容支持同时上传多个文件多个文件以数组形式存放在ctx.files中因为会在服务器临时目录落盘使用后应注意清理。9.2 stream 模式data为ReadStream可通过pipe等方式把数据流转发到其他WriteStream或TransformStream一次只上传一个文件ctx.files数组中只有一个文件对象不会在服务器生成临时文件取到内容后无需手动清理缓存。从源码看两种模式的分流点位于 packages/upload/src/middleware.tsmode file时文件被写入tmpdir下以upload_${Date.now()}.${Math.random()}为前缀、扩展名取自白名单的随机临时文件mode stream时则把二进制数据包装为Readable对象返回。注意部分函数计算平台不支持流式请求响应stream模式的实际可用性需参考对应平台能力说明。十、安全加固白名单与 MIME 校验10.1 扩展名白名单通过whitelist配置允许上传的扩展名配置为null将跳过扩展名校验。安全提示若设为null且使用file模式攻击者可能上传.php、.asp等后缀的 WebShell 实施攻击。当然由于组件会用随机生成的文件名落盘upload_时间戳.随机数.扩展名只要开发者不把临时文件地址返回给用户风险相对可控但仍不建议关闭校验。另外为防止恶意用户利用可被截断的扩展名绕过过滤组件在解析扩展名时会过滤二进制数据只保留0x2e英文点.、0x30-0x39数字0-9、0x61-0x7a小写字母a-z等字符其他字符自动忽略实现见 packages/upload/src/utils.ts 的formatExt。自 v3.14.0 起whitelist还可以传入函数根据请求上下文动态返回白名单// src/config/config.default.ts import { uploadWhiteList } from midwayjs/upload; export default { // ... upload: { whitelist: (ctx) { if (ctx.path /) { return [.jpg, .jpeg]; } else { return [.jpg]; } }, // ... }, };10.2 MIME 类型校验mimeTypeWhiteList攻击者可能把 WebShell 改名为.jpg绕过扩展名白名单在某些服务器环境下仍被当作脚本执行。为此组件提供mimeTypeWhiteList配置注意该参数默认没有值即默认不做 MIME 校验。规则形如扩展名 → MIME可多个// src/config/config.default.ts import { uploadWhiteList } from midwayjs/upload; export default { // ... upload: { // 扩展名白名单 whitelist: uploadWhiteList, // 仅允许以下文件类型上传 mimeTypeWhiteList: { .jpg: image/jpeg, // 可配置多个 MIME例如 .jpeg 允许 jpg 或 png .jpeg: [image/jpeg, image/png], .gif: image/gif, .bmp: image/bmp, .wbmp: image/vnd.wap.wbmp, .webp: image/webp, }, }, };也可以直接复用组件导出的DefaultUploadFileMimeType作为默认 MIME 校验规则它提供了.jpg、.png、.psd等常用扩展名的 MIME 映射见 packages/upload/src/constants.tsimport { uploadWhiteList, DefaultUploadFileMimeType } from midwayjs/upload; export default { upload: { whitelist: uploadWhiteList, mimeTypeWhiteList: DefaultUploadFileMimeType, }, };MIME 识别依赖file-type包当前仓库锁定版本21.3.4见 packages/upload/package.json其支持的文件类型范围请以该包文档为准。两点提示MIME 校验规则仅适用于modefile模式设置后需要读取文件内容进行匹配上传性能会略有影响但从安全角度仍建议尽量开启。自 v3.14.0 起mimeTypeWhiteList同样支持函数式动态返回export default { upload: { mimeTypeWhiteList: (ctx) { if (ctx.path /) { return { .jpg: image/jpeg }; } else { return { .jpeg: [image/jpeg, image/png] }; } }, }, };10.3 限定上传路径match/ignore组件启用后任何POST/PUT/DELETE/PATCH请求只要content-type是multipart/form-data且带boundary就会自动进入上传解析逻辑在临时目录创建文件缓存。这意味着恶意用户可以手动构造请求向任意普通接口上传文件导致服务器负载升高、缓存占满。因此强烈建议通过match或ignore配置限定允许解析上传的路径upload: { // 仅当路径命中 /api/upload 时才解析文件 match: /\/api\/upload/, // 或者反向排除ignore: [/^\/public\//], }从 packages/upload/src/middleware.ts 可以看到match与ignore是互斥的配置了match则只处理匹配路径否则才使用ignore排除路径。十一、临时文件与清理使用file模式时上传文件会保存在tmpdir指向的目录中可以通过两种方式清理自动清理cleanTimeout控制自动清理间隔默认5 * 60 * 10005 分钟设为0可关闭自动清理。底层由 packages/upload/src/utils.ts 的autoRemoveUploadTmpFile定时扫描临时目录删除创建时间ctimeMs早于cleanTimeout的文件。主动清理在代码中调用await ctx.cleanupRequestFiles()删除当前请求产生的全部临时文件。组件在onStop阶段会调用stopAutoRemoveUploadTmpFile停止定时清理任务见 packages/upload/src/configuration.ts避免进程退出时残留定时器。十二、安全警示清单结合组件源码与官方文档启用上传功能后请自查以下三点扩展名白名单whitelist是否开启设为null时可能被用于上传.php、.asp等 WebShell。路径限制是否配置了match或ignore否则普通POST/PUT接口可能被攻击者利用导致服务器负载与磁盘占用上升。文件类型校验是否配置mimeTypeWhiteList否则攻击者可能伪造文件类型绕过扩展名白名单。十三、测试验证仓库为上传组件提供了完整的测试覆盖见 packages/upload/testkoa.test.ts、express.test.ts、web.test.ts、faas.test.ts分别覆盖不同框架下stream与file两种模式koa.test.ts中还包含 MIME 校验、whitelist设为null、函数式白名单、allowFieldsDuplication重名字段等场景clean.test.ts则验证临时文件自动清理逻辑。如果你修改了上传相关配置可参考这些用例验证行为是否符合预期。结语通过midwayjs/hooks-upload与midwayjs/upload的组合Midway Hooks 让文件上传从处理ctx、解析multipart、管理临时文件的繁琐流程收敛为一行Upload(/api/upload)加一个useFiles()的纯函数接口。配合扩展名白名单、MIME 内容校验、match/ignore路径限制与自动清理机制即可在获得开发效率的同时守住安全底线。更多配置细节可继续阅读仓库内的 hooks/upload.md 与 extensions/upload.md。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Midway Hooks 一体化文件上传实战midwayjs/hooks-upload 接口声明、前端调用与源码原理Midway Hooks 一体化文件上传实战midwayjs/hooks upload 接口声明、前端调用与源码原理 导读 在 Midway Hooks 的后端微服务云原生Midway 通用文件上传组件 midwayjs/upload 实战指南file / stream 双模式与安全配置Midway 通用文件上传组件 midwayjs/upload 实战指南file / stream 双模式与安全配置 文件上传是 Web 应用与服务端接口最后端微服务云原生Midway 文件上传组件实战midwayjs/upload 的 file/stream 双模式、白名单校验与临时文件清理Midway 文件上传组件实战midwayjs/upload 的 file/stream 双模式、白名单校验与临时文件清理 midwayjs/upload后端微服务云原生上一篇CityFlow 快速城市交通仿真工具完全指南下一篇零 JavaScript 的 Flexbox CSS 框架Bulma 一个文件搞定整站样式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考