Title: 📂 分布式存储 Canonical: https://microi.net/doc/more/hdfs.html Page-Modified: 2026-11-04T06:35:06.000Z Source: published HTML document body; page revision is not a capability verification date. # 📂 分布式存储 平台分布式存储支持 阿里云 OSS/CDN、MinIO、亚马逊 S3,基于 SaaS 引擎配置,不同租户可使用不同存储方案。 --- ## 📖 介绍 特性 | 说明 | 支持存储 | 阿里云 OSS/CDN、MinIO、亚马逊 S3 | 配置驱动 | 基于 SaaS 引擎,不同租户可独立配置 | 可扩展 | 由表单引擎驱动,可自由扩展腾讯云、华为云等 | 热点资源分流 | 按完整日实际下载量选择公开资源,复制校验后优先读取 NAS / MinIO,达标偏好持续保留 | 源码位置 | GitHub · Microi.HDFS (https://github.com/itdos/microi.net/tree/master/Microi.Server/Microi.HDFS) / Gitee · Microi.HDFS (https://gitee.com/ITdos/microi.net/tree/master/Microi.Server/Microi.HDFS) | --- ## MCP 本机大文件与备用存储 支持 PrivateFileUpload/1 的后端提供普通文件分片入口。先用 MCP microi_manage_file_stream_upload 的 Capabilities 查看当前连接是否支持,再调用 microi_upload_file_stream。localFilePath 指 MCP 所在电脑的文件,文件按原始字节传输, 不会转成 Base64,也不会作为应用商城编译资产发布。 目标 | 实际存储 | Primary | 当前租户 SaaS 的主 HDFS 私有桶 | BackupPrivate | 当前租户 SaaS 的 MinIO 私有桶;公网绑定必须与系统设置 FileServerBak 相符 | BackupPublic | 显式授权公开的最终文件写入当前租户 MinIO 公有桶;上传分片、清单和回执仍在私有桶 | 默认 Primary,Primary / BackupPrivate 始终私有。备用上传不会切换 HDFS 或修改 OSS 配置,凭据由服务端读取, 调用方不能传入 Endpoint、桶名、密钥或服务器路径。仅交互式平台管理员可用;访问密钥会话不开放。 备用私桶必须与公桶分开,并通过只读桶策略检查;存在 Allow 桶策略时会拒绝上传,须先核对私有权限。 该分片入口校验 MinIO 私桶策略、阿里云 OSS 私有 ACL 与桶策略;S3 的完整私有 ACL 证明尚未接入该入口,会明确拒绝,不回退公有上传。BackupPublic 同样先核验暂存私桶;默认模式仅对最终经过校验且明确授权公开的单个对象追加匿名 GetObject,不开放整桶、前缀、列举或写入权限。下述专用导入桶模式独立使用受保护命名空间;两种模式都不修改私桶策略。 json ``` { "localFilePath": "D:/客户资料/监控原片.mp4", "target": "BackupPrivate", "requestId": "factory-original-video-001", "confirmExecution": "factory-original-video-001" } ``` 如果文件已明确授权所有人访问,可使用下面的公开导入。文件名和路径由协议限定在当前租户,不能指定自由目标地址: json ``` { "localFilePath": "D:/公开资料/示例录像.mp4", "target": "BackupPublic", "publishPublic": true, "requestId": "public-example-video-001", "confirmExecution": "PUBLIC:public-example-video-001" } ``` 公开目标缺少 publishPublic=true 或对应 PUBLIC: 确认时只返回预览,不执行上传。 不要把监控原片、员工人脸或样本库默认当作公开资料;上述授权只针对明确选定的文件。 传输格式硬上限为单文件 32 GiB、分片 16 MiB;实际文件大小仍受 SaaS 的 FileUploadMaxFileMB 及 FileUploadMaxRequestMB 限制,并共用 FileUploadDailyUserQuotaMB / FileUploadDailyTenantQuotaMB 的每日额度。DisableFileUpload 仍生效,不能凭管理员身份绕过;先核对 Capabilities 的实际上限。 普通上传的单文件和总请求绝对上限仍为 2 GiB;32 GiB 仅是分片清单格式能力,不代表租户可绕过限额。 Capabilities 不连接存储证明权限;StorageProviderSupported=false 时先停止,实际上传前还会核验私桶策略。 首次写入的分片预留日额度,已确认分片续传不重复预留,失败尝试按原上传规则不退款。 相同请求号绑定原始大小、SHA-256、分片清单、操作者、 租户及存储配置;结果未知时先 Status 回读,再用原请求号续传。换文件内容需要新请求号。 MCP 在本机保留不含凭据的恢复检查点;跨电脑恢复应保留回执中的 StorageBindingHash 并作为 expectedStorageBindingHash 传入,存储配置改变时拒绝续传。 Completed 仅在最终对象全量流式回读的原始字节数和 SHA-256 一致后返回。 需要独立复核时调用 Verify,不能用分片 ETag 代替原片 SHA-256。 私有目标回执保留 FilePathName、目标和哈希,不返回匿名下载链接;旧的主 HDFS 私有取址方法 不能直接用于备用私桶路径。 ### 大文件校验和发布的后台任务 后端 8.8.9 配合支持该协议的 MCP,Capabilities 返回 AsyncOperationsProtocol=PrivateFileOperation/1,将 Complete、Verify、 PublishPublic 和 ProbePublic 的完整文件处理交给已有持久任务运行。先读取实际能力; 版本号或工具已安装不能代替服务端能力回读。分片仍是固定 16 MiB,存储、限额和公开授权规则不变。 网关可能在文件完整回读结束前断开 HTTP。新的客户端通过短请求提交任务、查询进度, 不会为了避开超时跳过 SHA 校验,也不要求修改 API 域名或代理超时。 Code=2 只表示排队或执行中;只有最终 Code=1 且原文件字节、哈希、会话和存储绑定都一致, 才能记录对应操作成功。公开文件还须通过原有匿名读取证明。 客户端把任务请求号和恢复信息保存在本机检查点,不保存 Token 或存储密钥。 首次提交响应未知时,按同一请求号查询;不能换新请求号重复提交。进程重启后继续查询原任务, 后台执行重新检查当前管理员、会话 owner 和实际存储绑定。权限撤销、配置变化、取消或失败, 均不作为成功返回。取消请求不保证正在进行的存储调用立即停止,之后仍应读取任务终态。 旧后端仍使用原同步流程。如果实际调用仍被网关截断,保留会话和检查点,升级支持的后端与 MCP 后恢复; 不要删除已完成原片后重传。Status 是会话状态读取,不能替代完整文件验证或公开访问验证。 ### 专用公开导入桶 后端 8.8.8 起,可在 SaaS 引擎 → 对象存储 为当前租户配置 MinIOExplicitPublicBucketName,例如 itdos-public-imports。它是同一 MinIO 与 FileServerBak 域名下的专用用途桶,不能等于既有公桶或私桶;默认空继续原协议,不复制或继承主租户值。 不要修改 MinIOPublicBucketName 来迁移其它业务。 该模式解决既有桶策略接近 MinIO 20 KiB 上限时继续逐对象追加授权的问题。 后端只为自己创建且有私有签名回执的专用桶设置一条当前租户公开命名空间的 GetObject 规则, 不开放列举或写入,也不改旧桶策略。未知已有桶即使为空也拒绝接管;不得手动提前创建同名桶。 PublishPublic 仍要求原签名 Completed、当前管理员和明确公开意图。 先完整回读原片,再通过绑定 ETag/版本的服务端 COPY 写入受保护私有暂存,验证 SHA 后提升到专用桶。 普通上传、复制、移动和删除入口不能写入该暂存或用途桶。目标全量 SHA 与匿名 HEAD/首块 Range 均通过,才返回公开链接。 旧 Completed 会话无需重传,来源绑定保持原值,公开目标另有 PublicStorageBindingHash。 COPY 响应未知先只读核对目标;相同内容复用,不同内容拒绝覆盖。 创建请求在写入前失败且桶不存在可恢复;创建响应未知但桶已经出现时返回 BucketCreationNeedsReconciliation,需核对真实创建归属,不能自动接管。 ProbePublic 在专用模式仍只读,不创建桶、复制文件、写策略或补回执。 PromotionStageCleanupComplete=false 表示仅本会话的私有提升暂存尚未清理, 原 PublishPublic 重试可继续核对并清理;公开对象和原始来源不随清理删除。 该协议保留私有签名证明用于审计,存储容量应同时计入原始来源、公开副本和暂存峰值。 固定受管入口 platform-vision-media 可调用 V8.Method.GetCompletedPublicUploadProof({SessionId})。当前租户管理员必须同时是原会话 owner; 后端重新验证原片、目标和匿名可读性,返回 ObjectId/Status/Bytes/Sha256/PublicUrl/StorageBindingHash 及原签名清单的 BlockBytes=16777216/BlockHashes。不接收客户端自报 URL、摘要、桶名或用户身份。 8.8.9 的完整证明也由持久任务生成:首次请求可返回 Code=2,调用方须等待后重试原会话。 证明只在同一租户、当前管理员 owner、原始及目标存储绑定下短期复用,成功后有效期为 5 分钟; 到期重新验证,不能把本地旧回执当作新证明。MCP 可查询该受管证明任务的进度,不能通过公开排队接口 创建证明、取得证明正文或自行指定块摘要。媒体登记仍由受管 platform-vision-media 完成。 内部证明因临时故障失败后,可在至少 15 秒冷却后由该受管入口重新验证同一任务,保留旧失败状态记录; 只读查询不会启动重试,已取消任务也不会自动重启。公开发布写操作不套用此证明重试规则。 ### 公开授权与失败恢复 BackupPublic 自动公开要求后端 8.8.6 及支持此动作的 MCP 工具;先回读 Capabilities 的 PublicObjectPublicationProtocol=ExplicitPublicObjectRead/1。8.8.5 能完成存储,但不能为原来没有匿名权限的新对象补授权。 未配置专用用途桶时,新版上传工具完成后执行 PublishPublic:核验签名 Completed 会话与完整原片 SHA, 在与热点分流共用的存储租约下保留原桶策略,仅追加该最终对象的匿名 GetObject, 再次校验管理员、存储绑定与完整字节,再对可信 FileServerBak 下该对象进行匿名 HEAD 和首段 Range 校验,成功才返回 PublicUrl。 失败时用新租约撤销本次可能新增的精确授权并回报 PublicPolicyRollbackConfirmed;调用前已存在且核实的授权保持。 共享租约和写前比较用于协调平台写入者,不代表 MinIO 提供对外部管理员策略修改的原子 If-Match。 匿名校验覆盖对象总大小及前 16 MiB(小文件为全文件)原始哈希,不代表已匿名下载整段大视频; 完整文件 SHA-256 来自服务端存储回读。探测不携带凭据或 Cookie、不跟随跳转;大文件忽略 Range 返回 200 时立即拒绝, 不会读取整段文件。 PublicReadbackVerified=false 返回失败但保留 Status=Completed 及字节证据, 可沿原请求号重试,或调用 microi_manage_file_stream_upload action=PublishPublic,传原 sessionId、 storageBindingHash、target=BackupPublic、publishPublic=true 和 confirmExecution=PUBLIC:原sessionId。 此动作只处理签名回执指定的最终对象,不能传任意对象键;已完成会话超过 7 天也可重新授权校验,无需重传。 ProbePublic 仍为只读复核,不修改权限;独立 Complete 只完成存储步骤,不承诺匿名链接。不得自行拼接链接冒充匿名读取通过。 支持策略诊断的后端会附带 PublicPolicyDiagnostics:仅返回固定阶段、原因码、SDK 安全码、策略字节数、语句/资源/重复资源计数和只读状态, 不返回策略正文、资源清单、凭据或异常文本。仍须先验证当前管理员、原会话归属、存储绑定和完整文件 SHA。 ReadPolicy 表示读取策略失败,ValidatePolicy 可区分格式、容量与受管语句冲突;未知错误保留 Unknown,不能据此猜测 Redis 或 NAS 故障。 诊断只执行策略读取与规则检查,不授权或撤权。旧存储适配器不支持时返回 UnsupportedDiagnostics, 仍继续原有匿名 HEAD/Range 验证。PolicyValidationPassed 或 GrantPresent 不能替代 PublicReadbackVerified; 策略检查失败也不抹掉独立匿名检查结果。公开授权失败回执沿用同样的脱敏诊断,不改变原来的租约、回读与回滚规则。 未完成会话 7 天后停止接收数据,可用 Abort 清理该会话的临时分片;完成时也会清理分片。 CleanupComplete=false 表示清理尚未确认,重试 Complete 或 Abort 只清理自有临时对象, 不会删除已完成原片。清单和完成回执保留用于审计,文件内容不写入 MCP 日志。 已有 microi_import_external_attachment.sourcePath 仍指 API 服务账号可见的服务器路径或 UNC, 并沿当前主 HDFS 上传。SaaS 配置了 MinIO,并不意味着旧工具会自动写入备用私桶。 ## PC 与小程序图片互通 图片控件按字段 Config.ImgUpload.Multiple 保存单图对象或多图数组,MaxCount 只限制多图数量。历史单元素数组在单图控件中仍可读取。 上传请求携带字段授权上下文和 Limit,真实桶由后端校验;上传成功后保留实际 Limit 与稳定 Path,临时签名地址只用于运行时预览。历史图片没有 Limit 时按字段配置取址;图片明确为私有时必须授权签名,不能因字段当前为公有而拼接公有 FileServer。因此图片互通需要同时更新 PC 前端和小程序客户端,已有私有照片无需改成公有文件。 ## 旧移动端上传返回兼容 更新支持此功能的后端 Docker 镜像(8.3.4 及以上)并安装新版“系统设置”应用后,在 系统设置 → 开发配置 → 接口、文件与运行环境 开启“兼容平台旧版本”。 字段为 CompatiblePlatformOldVersion,默认关闭;空值与缺失字段也按关闭处理。 POST /api/HDFS/upload 保留 Code/Data 信封。关闭兼容时,单文件返回对象, Multiple=true 返回数组;开启后,单文件(包括 Multiple=false)也统一返回一项数组。 每个文件对象仍保留新版属性,并额外补充以下旧属性: 旧属性 | 来源或含义 | url、path、id | 对应本次上传的 Url、Path、Id | name | 不含最后一个扩展名的文件名,例如 前.jpg 返回 前 | size | 实际展示文件的 Size,不是压缩前体积 | type | 图片 image、视频 video、音频 audio、其它 file | duration | 已有时长,未提供时为 0 | uploading、progress | 上传成功后分别为 false、100 | 旧地址优先执行 platform-hdfs-upload 接口引擎;只有当前租户主库确认地址、别名及固定 Key 均不存在时,才执行编译的安全上传兜底。引擎被禁用、禁止 HTTP、无权限、数据库异常 或执行失败都会直接返回错误,不会再次上传。/api/Upload 与 /apiengine/platform-hdfs-upload 复用同一入口。 长期定制返回请编辑 platform-hdfs-upload-hook。该 Hook 只在首次缺失时创建,应用更新 保留租户代码;主接口是 Managed 资源,重新安装会恢复官方正文。Hook 示例: javascript ``` var result = JSON.parse(JSON.stringify(V8.Param.Result)); var rows = Array.isArray(result.Data) ? result.Data : [result.Data]; for (var i = 0; i < rows.length; i++) rows[i].BusinessTag = 'custom'; return { Code: 1, UploadResult: result }; ``` 先做 JSON 转换,避免 Jint 将 .NET 包装对象误判为数组或普通对象,导致自定义属性没有写入每个文件。 兼容开关只改变成功返回的字段,不改变公私桶、目录授权、裁剪、配额或内容安全策略。 私有文件的 url 沿用本次短期授权地址;需要长期匿名访问时,仍须按下文配置公有上传权限。 ## 🔐 上传与私有文件安全 登录不等于文件授权 Token 只用于确认用户和租户。普通帐号不能因为持有 Token 就任意选择公有桶、列目录、读取私有桶裸路径或调用文件管理接口;标准表单字段是否写入公有桶,由服务端回读该字段的“禁止匿名访问”配置后决定。 ### 表单字段如何决定公有桶或私有桶 表单设计器中 ImgUpload(图片上传)、FileUpload(文件上传)和 RichText(富文本)的 禁止匿名访问 是字段级存储策略,不是超级管理员专属权限: 字段权威配置 | 服务端实际策略 | 典型用途 | 未勾选“禁止匿名访问”(Limit=false) | 公有桶,可通过稳定公共地址访问 | 商品图片、官网内容、公开附件 | 勾选“禁止匿名访问”(Limit=true) | 私有桶,读取时重新校验业务权限并签发短效地址 | 合同、内部资料、个人文件 | 普通用户只要拥有当前菜单和表对应的新增或编辑动作权限,也可以按字段配置上传到公有桶。2026 年 7 月曾有一版安全加固把所有非超级管理员的交互式上传统一改成私有桶,这会覆盖字段设计器的合法配置,属于过度收紧;现已改为“客户端提供上下文、后端权威回查”。 PC 表单的图片、文件和富文本上传会提交 FormEngineKey + FieldId + SysMenuId;编辑已有记录时还会提交 FormDataId,TableChild 会附带父子授权上下文。服务端随后: - 按新增或编辑动作校验当前用户、角色、菜单、表和动作权限; - 从当前租户重新读取 diy_field.Component 与 diy_field.Config; - 只允许 ImgUpload → img、FileUpload → file、RichText → editor 三种对应目录; - 用字段中的 Limit 覆盖请求值,再返回实际 Limit 和可用预览地址。 因此篡改浏览器请求里的 Limit=false、Path、FieldId 或菜单 Id 都不能绕过授权。没有完整、可验证字段上下文的普通 HTTP/旧自定义上传,默认限制到私有安全目录;需要兼容旧客户端的固定业务目录时,由管理员配置下文的目录授权,不要求逐个修改旧客户端。老 ImgUpload / FileUpload 字段没有保存 Limit 时兼容为未勾选,即公有桶;老 RichText 字段没有上传配置时默认私有,避免升级后意外公开正文附件。 微信小程序内容安全流程是额外的强制收紧:待审图片即使属于公有字段,也必须先进入私有隔离区,审核通过后才允许写入业务字段;是否最终转为公共发布资产,仍需经过受控发布流程。图片裁剪或压缩保留的 _origin 原图也始终写入私有桶。 ### 旧移动端、定制页面的目录与角色授权 后端 v8.2.8+ 支持普通系统设置 sys_config.HdfsUploadRules。更新官方“系统设置”应用后,在 系统设置 → 开发配置 → 接口、文件与运行环境 → 文件上传权限 中维护规则。这是普通配置,不是后端私有设置或密钥;规则是否能被读取不构成授权,只有管理员维护的规则和后端读取的有效角色才构成授权事实。 规则列 | 填写方式 | 允许目录 Path | 相对业务目录或通配符,如 files/{inspection,quality}/**;不要填域名、桶名或租户前缀。区分大小写,兼容首尾 /;通配符需后端 v8.2.9+ | 授权角色Id数组 RoleIds | 如 ["角色Id1","角色Id2"];使用角色管理中的真实 Id,不是角色名称 | 所有登录用户 AllAuthenticated | 仅该目录确需所有有效登录用户上传时打开;打开后可不填角色 | 包含子目录 IncludeSubdirectories | 默认关闭;打开后也允许已匹配目录下的全部子目录。不改变目录层级边界;规则已用尾部 /** 时无需再打开 | 允许公有文件 AllowPublic | 默认关闭。只有打开且请求明确为 Limit=false 才写公有桶;请求私有时始终保持私有 | 例如,只允许指定角色向一个既有目录及其子目录上传公开图片: json ``` [ { "Path": "files/inspection", "RoleIds": ["替换为真实角色Id"], "AllAuthenticated": false, "IncludeSubdirectories": true, "AllowPublic": true } ] ``` 排查“普通用户只能上传到平台预定义的文件目录”时,先在浏览器网络面板查看失败请求实际提交的 Path、Limit,检查是否把同一目录同时作为 Path 和 path 两个 multipart 字段提交。客户端应只提交一个 Path;修复后的后端会合并值相同的重复字段,值不同时明确拒绝。确认请求目录后再添加最小授权,避免为了绕过重复参数导致的误判而扩大规则。系统设置保存沿用共享缓存失效机制,下一次上传重新读取规则,无需重启;已经完成的上传不会被追溯删除。规则为空、未命中或被删除时,恢复原有 file / img / avatar / editor 私有安全目录策略。最多 128 条规则,错误规则在新版表单引擎保存时会被拒绝。 规则只适用于没有表单字段上下文的已登录交互式上传,不改变标准字段上传的表/菜单/行授权;半套字段上下文仍拒绝。它不授予文件读取、删除、列目录、应用发布或跨租户权限;实际上传路径不能是根目录、包含通配符、穿越路径、micro-app、ai-app*、app-store、数据库备份和 _origin 等保留路径。即使管理员配置 **,这些保护仍优先执行。访问密钥会话不能借此获取目录权限。租户停用上传、内容检测、大小、数量及每日配额限制仍然生效,头像、待审图片及原图不会因此转为公有。 应用包只增加可空字段与界面,不附带业务授权数据,更新应用不会覆盖租户已经配置的规则。不要为了快速修复而提高普通用户等级,也不要删除后端目录校验。 应用需要初始化专用私有上传目录时,应由应用自己的 Managed 接口在当前租户管理员首次运行时执行:从目标租户的权威角色映射取得角色 Id,锁定唯一启用的系统配置,逐项保留既有 JSON 规则和未知属性,只追加缺失的应用私有前缀。配置比较交换、角色映射、初始化任务和租户 Hook 必须共享事务;失败全部回滚,缓存只在提交后失效。同一初始化任务重放不再次修改配置,管理员后来删除或禁用的规则不能被重放恢复。安装包的说明文件并不代表导入器已自动执行初始化步骤;真实入口和验收结果应明确记录。 #### 目录通配符(后端 v8.2.9+) 可组合以下格式,一行即可覆盖一组业务目录。规则只匹配请求的 Path 目录,不匹配上传文件名,也不匹配服务端后来追加的年月目录。 格式 | 示例 | 匹配含义 | * | files/inspect* | 当前层内零个或多个字符,例如 files/inspection;不跨 / | ** | files/** | 零个或多个目录层级,包括 files 本身及任意深度子目录 | 中间 ** | files/**/photos | 匹配 files/photos、files/a/b/photos;** 必须独占一层 | ? | workshop/line? | 当前层内一个字符,例如 line1,不匹配 line12 | [abc] / [a-z] | files/202[0-9] | 字符集合或范围,例如 files/2026 | [!abc] / [^abc] | files/[!0-9]* | 首字符不在该集合内,例如 files/photos,不匹配 files/9photos | {A,B} | files/{inspection,quality}/** | 任一候选分支及其子目录;可嵌套、可与其它格式组合 | * 单独使用只匹配一级业务目录;** 单独使用会匹配全部非保留业务目录,范围较大,应优先选择明确的业务前缀并绑定最小角色。多个命中的有效角色规则按授权并集处理:任一匹配规则允许公有且请求 Limit=false 时可公开;请求 Limit=true、头像、待审图片仍保持私有。 单条规则最多 512 个字符,花括号最多嵌套 4 层、展开 32 个候选、4096 个字符令牌。非法括号、倒序范围、空分支、ab**、*** 会在保存时被拒绝。匹配使用有界动态规划,不执行正则表达式、SQL %/_ 通配或脚本;不支持反斜杠转义。服务端仅对解析后的语法做有界缓存,不缓存权限判定,角色与系统设置仍从当前租户的共享权限/配置缓存读取。 升级顺序:先更新全部后端节点到 v8.2.9+,再启用通配规则;v8.2.8 只支持精确目录和“包含子目录”,与新节点混用时可能拒绝新语法。旧移动端请求格式无需变化。 ### 图片默认压缩与私有原图 ImgUpload、PC、UniApp/H5/小程序和普通 MCP 图片上传在未传 Preview 时默认开启压缩。平台展示图默认控制在约 500 KB 以内,最长边不超过 1920 px;字段设计器的新图片字段同样默认开启。只有明确传入 Preview=false 才关闭压缩,旧移动端漏传参数不会关闭。 压缩前,后端会先把原始字节保存到当前租户 HDFS 私有桶的同名 _origin 对象,再使用跨平台图片库生成展示图并写入目标公有或私有位置。因此对外页面加载的是几百 KB 的展示图,原始高清图仍只在私有桶保留。上传结果的 Size 表示展示图实际体积,OriginalSize 用于管理员审计。 ### 表单引擎裁剪原图协议 ImgUpload.Crop.Enabled=true 时,PC 前端在同一个 multipart/form-data 请求中传入两份同名数据:普通 file 是裁剪结果,MicroiOriginalFile 是裁剪前未改动原图,并传入 CropEnabled=true。该特殊字段是平台内部协议,业务 V8 不应手工伪造。 Crop.Enabled 只决定“上传前裁剪”的默认状态。表单用户关闭运行时开关或在裁剪工作台点击“不裁剪直接上传”时,不传 CropEnabled/MicroiOriginalFile,继续走普通图片上传协议;若 Preview 开启或缺失,普通压缩链路仍会先把原始字节保存在私有 _origin 对象中。 后端会核对两者一一同名,并将裁剪图与原图的字节数一起计入单次大小和每日配额。写入顺序固定为“原图写入私有 _origin 对象 → 可选压缩裁剪图 → 写入业务展示图”。原图写入失败时必须失败关闭,不得先发布裁剪图。返回的 Cropped=true 和 OriginalStored=true 只表示服务端执行结果,不暴露原图对象路径。 ### 富文本附件的长期引用 RichText.Limit=false 用于官网公告、商品详情等公开正文:图片、视频和附件写入公有桶,正文保存可长期访问的公有 URL。Limit=true 用于内部内容:正文只保存 /__microi_richtext_private__/... 稳定对象标识,不保存对象存储签名 URL、审计代理 Ticket 或 DiyToken。 查看或编辑已有记录时,富文本组件从真实 img/video/source.src 和 a.href 收集稳定标识,携带 FormEngineKey + FormDataId + FieldId + SysMenuId 调用 /apiengine/platform-private-file-url。后端重新校验用户、租户、菜单、表、行、字段以及正文中的精确引用,再返回本次页面使用的短效审计代理 URL;关闭后重新打开会换取新地址,因此短效地址过期不会污染数据库内容。历史正文直接保存 FileServer URL 时,后端只对真实媒体属性中的精确同路径引用提供兼容,不把普通文字、data-href/data-src 或脚本标签当作授权依据。 私有富文本不能直接匿名发布 外部网站没有当前后台记录的菜单与行权限上下文,不能解析私有标识。需要匿名展示的文章应在表单设计器中取消 RichText 的“禁止匿名访问”;有新增或编辑权限的普通用户随后也会按该字段的权威配置上传到公有桶。仅修改请求参数不能改变存储策略。 ### 需要立即撤回的公开媒体 公开桶链接适合长期公开资源,删除源对象并不能证明 CDN 缓存已经失效。需要撤稿后使旧链接停止返回字节的媒体,应保存在私有桶,由专用业务接口根据当前租户、真实批准版本及其精确媒体引用授权,再返回受限的图片字节。接口不能接受任意对象路径,不能把签名 URL 或重定向当作撤回保证;读取前后均应复核批准、发布、许可和撤回状态,并返回 Cache-Control: no-store。接口外部地址只包含稳定的业务记录标识,不携带 Token 或私有存储路径。 撤回先在共享数据库事务中使公开授权失效并记录稳定请求键的待办,提交后再执行对象删除;存储结果未知时按原请求键探查和续作,禁止重新上传。历史公开 URL 只有在实际 CDN 失效任务完成、缓存入口独立回读旧 URL 不再返回媒体字节后,才能记作撤回完成。没有该能力或证据时应保留“源对象已删除,CDN 撤回未确认”的状态,不能用隐藏页面、Offline 或源站 404 替代验收。已经被第三方下载的副本不属于本系统可以撤回的范围。 压缩失败不会发布原图 图片解码、压缩或编码失败时接口直接返回错误;平台不会再静默把几十 MB 的未压缩原图上传到公开桶。历史大图迁移也必须先补齐私有原图、上传新展示图、更新并回读全部业务引用,确认无引用后才能删除旧公有对象。 ### 微信小程序图片与资料内容安全 微信小程序中的头像及其它用户图片统一通过 V8.uploadFile 上传;多图组件使用 V8.uploadFiles,最多并发上传 3 张,并通过受管接口引擎 mci-wechat-content-status-batch 合并查询本人最多 20 条审核状态。小程序运行时 SDK 为每次上传获取独立的一次性 wx.login code,后端把图片保存在私有隔离区,并调用微信 mediaCheckAsync;只有异步回调明确返回 pass 后,SDK 才把对应文件值写入表单。单张失败不影响其它已通过图片,轮询采用有限次数、总超时和退避间隔,不允许无限请求。review、risky、回调超时、配置缺失或 Redis 故障均失败关闭。用户侧只提示“你发布的内容含违规信息,请修改后重试”,不得显示命中标签、策略或概率。 个人资料保存还会在服务端再次核对审核记录、图片路径和提交人,并用 msgSecCheck 检查姓名、实名和个人简介。因此不能通过跳过前端轮询或直接请求 UptSysUser 绕过检测。 SaaS 引擎“微信小程序”Tab 提供以下租户配置;它们属于敏感配置,不得写入 appsettings.json、环境变量、UniApp 源码、接口引擎或日志。相关字段应随官方应用商城资源交付,不再为此新增 Microi.Upgrade 定制迁移: SaaS 引擎字段 | 说明 | WeChatMiniProgramAppId | 当前小程序 AppId | WeChatMiniProgramAppSecret | 当前小程序 AppSecret,仅后端读取 | WeChatMiniProgramMessageToken | 微信消息推送签名 Token | WeChatMiniProgramAESKey | 可选;兼容/安全模式回调解密密钥(EncodingAESKey,43 位) | 在微信公众平台配置小程序消息推送 URL: text ``` https:///api/WeChatContentSecurity/Callback--OsClient---- ``` 如果第三方设置页确认支持查询参数,也可以使用 /api/WeChatContentSecurity/Callback?OsClient=;禁止使用 ?o=。Token 必须与 WeChatMiniProgramMessageToken 完全一致;可先使用明文模式,启用兼容或安全模式时同时填写 EncodingAESKey。API 域名必须是微信可访问的 HTTPS 地址,负载均衡后的所有节点连接同一 Redis。接入依据见微信官方 mediaCheckAsync (https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.mediaCheckAsync.html) 与 msgSecCheck (https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.msgSecCheck.html) 文档。 ### 租户动态限制、灾难保护上限与每日配额 普通 HTTP、FormEngine、V8 和移动端上传入口共用服务端限制。上传限制分为三层:租户业务配置、平台独立灾难保护上限、HTTP 请求解析上限。表单字段的“禁止匿名访问”只决定公有桶或私有桶,不能放大文件大小、数量、每日配额或 HTTP 上限。受能力鉴权的 AI 应用资产协议 v3 使用下文独立的断点续传链路。 业务值只按 sys_osclients 当前租户 → 代码默认值解析,管理员无需维护额外环境变量或修改 appsettings.json: SaaS 引擎字段 | 代码默认值 | DisableFileUpload | false(关闭,即允许上传) | FileUploadMaxFileMB | 500 MB | FileUploadMaxRequestMB | 500 MB | FileUploadMaxCount | 10 | FileUploadDailyUserQuotaMB | 2048 MB | FileUploadDailyTenantQuotaMB | 20480 MB | 业务值最终再与平台代码中的固定灾难保护上限取较小值:单文件 2048 MB、单次总量 2048 MB、单次 100 个文件、帐号和租户日额度各 10 TB。这些硬上限不是安装配置项,普通租户不能放大。管理员可以把单文件与单次总量配置为 1024 MB 或 2048 MB;空值使用 500 MB 默认值。 Kestrel HTTP 正文和 Multipart 接收硬顶统一为 2048 MB,普通表单单值硬顶为 128 MB;它们是所有租户共享的安全边界,不要求安装者再配置环境变量。租户业务配置仍负责其自身的最终额度,反向代理还必须允许请求进入 API。 #### nginx 413 与大文件上传 413 Content Too Large 如果响应正文是 nginx 的 HTML,表示请求尚未进入吾码 API,SaaS 引擎缓存、HDFS Controller 和全局异常处理都没有机会执行。实际可上传大小是“nginx → Kestrel HTTP → Multipart → 租户单文件/单次额度 → Absolute 灾难保护”各层上限的最小值。禁止只提高 sys_osclients.FileUploadMaxFileMB,也禁止用 client_max_body_size 0 关闭网关保护。 例如需要让普通 multipart 上传覆盖平台 2 GB 硬顶,应在 API 域名的 nginx server 块中配置: nginx ``` # 支持平台2GB硬顶,并为multipart封装留出余量;禁止配置为0取消保护 client_max_body_size 2112m; # 慢速大文件上传的请求体读取空闲超时 client_body_timeout 600s; # 避免 nginx 先把整个大文件重复缓冲到本机磁盘;HDFS/API 仍执行自身校验。 proxy_request_buffering off; # 分块请求关闭缓冲时使用HTTP/1.1,并给上游读写保留足够时间 proxy_http_version 1.1; proxy_connect_timeout 60s; proxy_send_timeout 600s; proxy_read_timeout 600s; # nginx 自己拒绝的请求无法进入 ASP.NET Core,需在代理层保持吾码 DosResult 合约。 error_page 413 = @microi_upload_too_large; location @microi_upload_too_large { default_type application/json; charset utf-8; add_header Cache-Control "no-store" always; # 跨域部署时,此处必须显式复用正常API代理的CORS白名单/include;不能依赖后端补响应头。 return 200 '{"Code":0,"Data":null,"Msg":"上传请求在进入吾码 HDFS 前已超过反向代理请求体上限。SaaS 引擎上传额度不能放大 nginx/Kestrel/Multipart 上限;请运维同步提高各层上限后重试。","DataAppend":{"ErrorType":"UploadRequestTooLarge","Layer":"ReverseProxy"}}'; } ``` 这些 proxy_* 指令可放在 API 域名的 server 层供代理 location 继承,也可合并进现有的 location ^~ /;不要新建第二个重复 location。proxy_request_buffering off 只关闭 nginx 预缓冲,不能用响应方向的 proxy_buffering off 代替,也不会绕过 API/HDFS 校验。 修改 nginx 后先执行 nginx -t,成功后再 reload。吾码普通上传 API 已内置 2048 MB HTTP/Multipart 接收硬顶;无需增加上传相关环境变量。普通单请求仍不能突破平台固定的单文件 2048 MB、单次总量 2048 MB 灾难保护上限。AI 应用资产协议 v3 与 SaaS 租户数据库 ZIP 协议每次只发送一个有界原始分片,不继承整文件单请求上限。若前面还有 CDN、WAF、负载均衡或 Ingress,还要同步检查这些上游的请求体和空闲超时限制。 ##### 创建租户数据库 ZIP 创建 SaaS 租户时,数据库 ZIP 不再走整包 /api/HDFS/UniappUpload: - 平台先创建与租户、用户、文件大小和摘要绑定的上传会话,再以默认 16 MB(服务端允许 1–32 MB)原始分片上传;每片与整包都做 SHA-256 强回读。 - 刷新页面、网络断开或浏览器重开后,重新选择同一文件会读取服务器状态并只补传缺失分片。单片接口接收上限为 64 MB,所以常见 100 MB 代理上限不会阻断 500 MB、1 GB 或 2 GB 的逻辑文件。 - 完成合并后,租户开通服务从私有 HDFS 临时文件流式解压并执行 SQL,避免 ZIP 与解压后的脚本同时驻留内存;连续 INSERT / REPLACE 按最多 500 条或 1 MB 使用有界事务批次执行。 - 通知中心不会按数据行写日志,而是限频更新读取量、批次、语句数、吞吐和预计剩余时间,并仅在阶段变化或每 5% 进度追加一条有界执行记录。 请求进入吾码 API 后,如果 Kestrel 或 Multipart 再触发超限,全局异常处理会返回 HTTP 200、Code=0、DataAppend.ErrorType=UploadRequestTooLarge,并在响应头给出 X-Microi-Upload-Max-Request-MB 与 X-Microi-Upload-Max-Multipart-MB,方便定位实际生效的 API 启动配置。 Upgrade16/Upgrade35 会在 sys_osclients 为每个租户补齐下列现行可空字段: SaaS 引擎字段 | 作用 | 空值行为 | DisableFileUpload | 是否关闭当前租户交互式上传 | 空值/无效值/0 均为不关闭,即允许上传 | FileUploadMaxFileMB | 单文件大小 | 使用代码默认值 | FileUploadMaxRequestMB | 单次全部文件大小 | 使用代码默认值 | FileUploadMaxCount | 单次文件数量 | 使用代码默认值 | FileUploadDailyUserQuotaMB | 单帐号每日额度 | 使用平台默认额度 | FileUploadDailyTenantQuotaMB | 单租户每日额度 | 使用平台默认额度 | 租户配置可以高于代码业务默认值,但普通上传不能突破平台固定灾难保护、HTTP/Multipart/Form 解析上限以及反向代理限制。DisableFileUpload=1 会停止该租户普通上传和应用资产断点续传,而不是关闭安全检查;协议 v3 只移除产品级字节上限,不绕过身份、能力、版本、哈希和审计。修改 SaaS 引擎配置后应通过平台现有的租户重载流程刷新共享 Redis 配置,使所有 API 节点生效。 #### “当前租户已停用文件上传”如何处理 新版以 DisableFileUpload 为事实源:缺列以外的空值、无法解析或 0/false 都表示允许上传,只有 1/true 才禁止。仅当运行节点尚未升级、物理表没有新字段时,才回退读取旧 FileUploadEnabled。因此看到“当前租户已关闭文件上传”时,先检查实际生效记录的负向开关,不要先把问题归因于 MinIO/HDFS。 - 在 SaaS 引擎查询目标 OsClient,同时核对当前后端进程的 OsClientType、OsClientNetwork。同一租户存在内网、外网或开发/生产多条记录时,只修改当前服务器实际命中的启用记录,不能凭租户名称批量覆盖其它环境。 - 将该记录的 DisableFileUpload 关闭为 0 并保存。返回结果中的 DataAppend.ConfigField=DisableFileUpload、DataAppend.ExpectedValue=0、DataAppend.OsClient 可用于确认目标。 - 等待 SaaS 配置重载和共享 Redis 发布订阅完成;多 API 节点应全部收到同一版本,不能只清某个节点的进程内缓存。 - 分别用一个很小的公有图片和一个私有文件做真实上传、读取测试。若错误变成 endpoint、bucket、签名或 Invalid URI,再按 MinIO/HDFS 配置排查;不要继续修改开关,也不要删除每日配额 Redis Key。 平台超级管理员也可按下文 MCP 流程精确更新,但写入前后都要回读同一组 OsClient + OsClientType + OsClientNetwork 数据。禁止为了消除提示而把所有租户、所有网络环境无差别改为允许。 帐号与租户额度使用共享 Redis 原子预留,适用于多 API 节点;Redis 不可用时上传失败关闭,不会降级成无限上传。额度按 UTC 日期统计,为避免并发重试绕过限制,上传后续失败也不退回已预留额度。反向代理、Ingress/IIS 还应设置不高于平台配置的请求体限制。 每日配额用于阻断短时间滥用,不等于租户全生命周期容量上限。生产对象存储还必须按租户或桶配置独立的总容量/账单告警与生命周期规则,并定期以对象存储实际用量对账;否则用户即使每天都低于应用配额,长期累计仍可能消耗大量空间。应用层 Redis 计数不能代替存储提供方的硬容量边界。 #### 通过 MCP 调整租户上传配额 平台超级管理员可以通过标准 MCP 工具修改 sys_osclients 中的六项租户业务配置,无需编写临时 SQL 或清空 Redis。容量字段单位均为 MB,例如 20 GB 应写为 20480。 - 先调用 microi_get_table_data 查询 sys_osclients,按目标 OsClient 和 IsEnable=1 筛选,并回读 Id、OsClientType、OsClientNetwork 以及六个 FileUpload* 字段。 - 同一租户可能同时存在 Internal、Internet 等多条启用记录。对每条记录调用 microi_update_form_data,传入 tableName: "sys_osclients"、包含 Id 的字段补丁以及 confirmExecution: "sys_osclients",避免负载均衡节点读取到不同配置。 - 再次调用 microi_get_table_data 逐条回读。FormEngine 保存 sys_osclients 后会触发 SaaS 运行配置重载;等待重载完成后,用一次真实小文件上传验证错误提示中的有效额度或上传结果。 示例字段补丁: json ``` { "Id": "", "FileUploadDailyUserQuotaMB": 20480, "FileUploadDailyTenantQuotaMB": 20480 } ``` 提高配额不会清零当日已经预留的字节数,而是立即按“新上限减去今日已用量”计算剩余额度。每日计数按 UTC 日期切换(北京时间每日 08:00 进入新的 UTC 统计日);失败上传为防重试绕过也不会退回预留额度。除非用户明确授权事故处置,AI 不得删除共享 Redis 配额 Key。平台固定灾难保护、Kestrel/Multipart/Form 和反向代理限制不能通过租户侧 sys_osclients 或普通 MCP 表单更新突破。 #### AI 应用编译产物流式发布 Web、UniApp 和 MicroService 的真实编译目录应使用 MCP 工具 microi_publish_application_directory_stream 发布。该链路不会把文件转为 Base64,也不会让文件体进入接口引擎/Jint: - MCP 在本机按文件流计算 SHA-256,先拒绝符号链接、.git、node_modules、密钥/环境文件和超过 20000 个文件的异常目录;协议 v3 不设置 Microi 产品级文件/目录字节上限。 - 不超过 128 MiB 的文件兼容旧版单请求;更大文件自动创建确定性断点会话,默认以 16 MiB application/octet-stream 分片发送。每片校验精确 Content-Length、SHA-256,并从 HDFS 写后回读。 - 所有文件写完后,MCP 只提交路径、大小和摘要清单到 /api/V8Engine/FinalizeApplicationStreamPublish。API 回读不可变版本对象与完整性标记,再用对象存储服务端 CopyObject 投影到公开的版本目录;私有源码按同样相对目录保留最新版与历史快照。 - 固定根的非入口资源先切换,index.html 最后切换;同一应用使用跨节点分布式锁串行发布。随后刷新被覆写的 CDN 固定路径并等待刷新任务完成,从 CDN 回读入口及引用资源,才更新商城体验地址。 text ``` 公有桶历史版本:{tenant}/micro-app/{appKey}/v1.2.3/index.html 公有桶稳定地址:{tenant}/micro-app/{appKey}/index.html 私有桶源码版本:{tenant}/micro-app/{appKey}/v1.2.3/{sourceFile} 私有桶最新源码:{tenant}/micro-app/{appKey}/{sourceFile} ``` Web、UniApp 与 MicroService 均采用该目录,https://file.microi.net/{tenant}/micro-app/{appKey}/index.html 是公有桶投影入口。官网体验解析器优先使用已声明的 v3 稳定 API 路由;对于当前 FileServer(含旧官方域名别名)下、同租户同应用的已声明永久入口,改用可信 ApiBase 的 /micro-app/{tenant}/{appKey}/index.html。后端校验当前公开版本、源绑定、NAS 副本完整性及健康后,才能返回 FileServerBak;尚不满足条件时仍走原存储。官网不拼接 NAS 前缀,也不据 AppKey 猜公开入口。v3 内部不可变对象键仍作为校验源;数据库只保存路径、大小、SHA-256、版本和路由等元数据。失败后可用相同版本和摘要安全重试;历史目录若已有不同字节则拒绝覆盖。 v3 的 sys_microistore.PreviewUrl/PublicPublishPath 与版本 PreviewUrl 在数据库中使用以 / 开头的对象路径,发布完成态检查依赖该路径。官网接口仍可返回按租户 FileServer 生成的投影地址,官网消费者再按上述规则选择 API 入口;这不改变数据库中的发布元数据,也不代表后台工作台和历史文件直链已全部接入分流。不要为了统一展示而直接将这些内部字段改成绝对 URL。 官网首页、AI 应用列表和详情页共用体验地址解析器。明确的私有记录或跨租户身份不构造公开入口;签名资源、第三方地址和显式历史版本保持原资源语义,不能将旧版本改成 current/latest。历史安装包、应用商城 JSON 包、独立文件以及官网自身 JS/CSS 不因体验入口调整而自动走 NAS;只有对应资源的可信副本及消费者解析链均验收后,才能确认分流覆盖。 使用官方 static.itdos.com 时,发布端从当前租户“系统设置 → 安全与服务接入”的 Integration.Cdn.Aliyun.AccessKeyId/AccessKeySecret 读取 CDN 凭据;旧租户可沿用 Integration.Dns.Aliyun.* 或 SaaS 配置中的 AlidnsKeyId/AlidnsKeySecret。密钥必须成对配置,并具备阿里云 CDN 刷新与任务查询权限。刷新提交只会返回任务号,发布器需等任务全部为 Complete 且从 CDN 按大小、SHA-256 回读成功后才确认发布;凭据缺失或任务失败时会保留可恢复状态。阿里云默认每天有 URL 刷新配额,批量迁移前应核对当日配额与已用量。 迁移存量应用时,旧历史对象可能已缺失。只有商城编译包、仍可访问的当前体验路由或本地归档中的原始字节与冻结发布清单的大小、SHA-256 完全一致,才可补齐;无法恢复的历史版本保持原记录并列入待补清单。当前版本独立校验通过后可以先切换固定体验入口,不能把缺失的历史目录当成已归档版本。 几十 MB 不是 Jint 的固定内存上限,HDFS 本身也没有这种限制。旧发布流程的问题是先把二进制扩成约 4/3 大小的 Base64,再经 JSON、Jint 字符串和多层复制产生累计分配。普通小型 V8 上传可继续使用 V8.Method.Upload;真实编译目录和大型资产必须使用协议 v3。5 GiB 文件默认是 320 片,网络或进程重启后查询远端状态并只补缺片。每个会话在 mci_ai_app_file 以 StorageScope=ApplicationAssetMultipartSession 保留,管理员可从“系统引擎 → 超大文件上传记录”查看字节进度、分片数、心跳、错误与恢复建议。最终能力由协议技术边界、对象存储、磁盘、网关和网络共同决定,而不是普通表单的整文件上限。 调用示例: json ``` { "appIdOrKey": "microi-developer-toolbox", "versionNo": "v1.1.0", "directory": "D:/build/microi-developer-toolbox/dist", "entryPath": "index.html", "confirmExecution": "microi-developer-toolbox" } ``` 标准表单字段上传按本页前述权威字段配置选择公有桶或私有桶,普通用户无需成为超级管理员;后端会把目录固定为控件对应的 file、img 或 editor。没有可验证字段上下文、也未命中系统设置目录授权的普通交互式上传,仍强制写入私有桶,并且只能使用 file、img、avatar、editor 四个安全一级目录。管理员可通过前述 HdfsUploadRules 显式授权其它业务目录及公有上传;绝对路径和 .. 等非法路径始终拒绝。公有上传不等于信任客户端参数,而是服务端确认字段权限或当前用户的目录授权后作出的决定。 接口引擎、后端表单 V8 和平台内部任务调用 V8.Method.Upload 属于可信服务端普通上传,可以由业务代码选择安全路径和公私有桶,但仍受全局文件数量、单文件和单次总量硬限制。只有应用资产协议 v3 具有独立断点与无产品字节上限语义;浏览器、移动端和普通 HTTP 客户端不能通过伪造 _TrustedServerInvocation、Limit 或 Path 获得这种信任。 #### 已提交应用的 CDN 特殊字符别名修复 对象键、清单逻辑路径和浏览器 URL 是三个不同的值。例如,逻辑文件名 asset@subasset.json 对应的公开对象键应保留 @,浏览器 URL 可将它编码为 %40;若对象键本身含字面量 %40,访问这个旧对象的 URL 则需写成 %2540。不能把 URL 编码结果直接当作新的对象键,也不能在 HDFS 中对所有对象键统一反解码,否则会改变存量对象的身份。 当已提交的协议 v3 应用出现这类 CDN 投影缺口时,可由当前租户平台超级管理员通过受管接口引擎 ai_app_cdn_backfill 的 Action=RepairCommittedCdnAliases 补充别名。它只复制经过验证的既有公有对象,不接收文件体,不修改清单,也不删除或重命名原编码对象。v3 内部不可变对象、完整性标记、请求身份和运行清单摘要保持原样。 调用前必须回读当前商城记录与版本完整记录,并冻结以下参数;不得仅凭 AppKey 或上次运行的缓存值发起修复: 参数 | 绑定要求 | AppId、ConfirmAppId | 同一精确应用 Id;当前用户仍须满足 Level >= 9999 | ExpectedAppKey、ExpectedVersionId、ExpectedVersionNo | 当前应用及版本归属 | ExpectedRequestId、ExpectedRequestFingerprint、ExpectedRuntimeManifestHash | 该版本协议 v3 请求身份与完整运行清单摘要 | ExpectedCommittedPublishVersionId、ExpectedCommittedRuntimeManifestHash | 商城当前已提交版本指针及摘要 | ExpectedCurrentVersion、ExpectedPublishFence、ExpectedPublishRowVersion | 当前版本号计数、发布栅栏和行版本,使用规范十进制字符串,避免大整数精度丢失 | DryRun | 默认 true,只检查绑定与对象摘要;仅显式 false 执行复制 | AliasStart、AliasCount | 默认分别为 0、1;AliasStart 索引冻结清单内符合范围的原始 @ JSON 列表;AliasCount 为 1–5,不是全部资产的分页大小 | 服务端同时核对当前活动指针、版本归属、协议、版本 FencingToken、派生的 ReleasePrefix,以及商城和版本的发布状态;两者状态只接受 ProjectionPending、RepairRequired 或 Completed。完整清单按平台既定的 ordinal 路径排序,以 PathSha256Size、行间 LF、末尾无 LF 和 UTF-8 重新计算 SHA-256,后续回读还须确认冻结清单字符串未变。 允许补齐的路径仅为 assets//import/<两位十六进制>/<文件名>@<子资源>.json,且必须是非入口、公有运行 JSON,单文件不超过 1 MiB。清单中出现不支持的 @ 路径时整次拒绝,不静默跳过。私有或源码资源、百分号转义、绝对路径、目录穿越、自定义桶或目标路径、调用方上传内容均不属于此动作。 该维护动作还要求完整运行清单使用安全 ASCII 相对路径、恰好一个入口、最多 20000 项、单项不超过 2 GiB、合计不超过 4 GiB。这是此小范围修复动作的校验边界,不改变协议 v3 正常流式发布的容量语义。 对每个冻结逻辑路径 P,复制路径由服务端按当前租户推导,均使用 Limit:false: 用途 | 对象键 | 保留的既有来源 | {tenant}/micro-app/{AppKey}/{VersionNo}/{P 中的 @ 替换为字面量 %40} | 版本目录别名 | {tenant}/micro-app/{AppKey}/{VersionNo}/{原始 P} | 固定目录别名 | {tenant}/micro-app/{AppKey}/{原始 P} | 传给存储原子的必须是租户内对象路径,不能用 HTTPS 来源 URL 代替。每个资产最多产生版本目录和固定目录两次复制,因此一批最多 5 个资产、10 次复制。执行前先检查整批来源和目标:Sha256 是对象原始物理字节的摘要,与 Size 一同绑定冻结清单;WireSha256 是这些字节转为 Base64 文本后的历史兼容摘要,只校验摘要格式及来源、目标之间的一致性,不能要求它等于清单的原始字节摘要。已有目标字节相同则跳过,任何目标字节不同则整批拒绝,不覆盖。每次复制前后再次核对摘要和最新发布绑定。 CopyObject 当前没有原子的 create-if-absent/禁止覆盖参数,响应会明确返回 AtomicCreateOnly:false。前置检查能拒绝已观察到的冲突,不能消除“检查后、复制前”另一个写入者改动目标的窗口。修复期间不能有另一发布者或任意写入者竞争同一固定路径;既有后台投影可继续写入同一冻结版本的相同字节。指针、活动版本、栅栏、行版本或清单发生变化时,停止当前流程,核对已产生的副作用并重新取得契约,不能只替换预期参数强行续跑。 建议按以下顺序执行: - 回读当前绑定,对最多 5 个资产执行 DryRun:true,检查返回的来源、目标、摘要和大小。 - 使用同一组冻结绑定及游标执行 DryRun:false,记录 Attempted、Copied、RequiresReadback 和 NextAliasStart。 - 独立从版本及固定 CDN URL 回读实际字节、大小和 SHA-256。超时、未知结果或 Code=0 不能证明对象未写入;先回读已尝试路径,再决定是否重试。数据库事务回滚不能撤销 OSS 复制。 - 下一批重新回读当前事实并核对契约,再使用返回的 NextAliasStart。全部别名核对后,由原发布后台继续完成 CDN 刷新、公开回读和状态收敛。 此动作不刷新 CDN、不推进提交指针、不修改发布状态,也不将 ProjectionPending/RepairRequired 强制改成 Completed。复制成功不能代替刷新任务完成、公网字节一致和真实浏览器资源加载验收;旧 404 缓存也需通过原发布刷新流程处理。 ### 私有文件必须绑定业务记录 普通用户调用 /apiengine/platform-private-file-url 时,除文件相对路径外必须提交: 参数 | 说明 | FormEngineKey | 文件所属表名或表 Id | FormDataId | 文件所属业务记录 Id | FieldId | 保存该文件引用的 FileUpload / ImgUpload 字段 Id | SysMenuId | 当前用户实际进入的菜单 Id | 服务端依次校验:Token 与 OsClient 一致、用户拥有该菜单、菜单绑定目标表、菜单数据范围允许读取该记录、字段属于该表且是文件字段、字段值确实引用所请求路径。任一步失败都不会退回裸路径签名 URL;普通用户也不能直接获取私有文件的 Byte / Stream。私有文件链接仍为短期后端票据,不能持久化临时 URL。 接口引擎和后端表单 V8 可以在可信服务端上下文中调用 V8.Method.GetPrivateFileUrl({ FilePathName })。这不表示浏览器也能只传路径签名;普通客户端始终必须携带上表中的完整业务上下文。 文件列表、移动、重命名、删除、覆盖上传等管理接口仅允许 Level >= 9999 的平台超级管理员。业务用户删除附件应通过受保护的表单/业务接口完成,由服务端核对记录权限和字段引用后处理对象存储,不能直接开放文件管理 API。 富文本编辑器的远程图片抓取 catchimage 默认关闭,避免把平台变成任意 URL 下载器或内网探测代理。业务若需要采集远程资源,应使用单独的受控接口,并实现域名白名单、DNS/IP 校验、禁止跳转、超时、响应体上限和文件头校验。 ### 仅删除空目录标记 事故恢复只需移除一个目录占位对象时,使用专用 POST /api/HDFS/DeleteEmptyDirectoryMarker,显式传 FilePathName(保留末尾 /)与 Limit(true 私有桶、false 公有桶)。服务端读取 JSON 后强制 EmptyDirectoryOnly=true;客户端传 false 也不能改成递归删除。旧后端缺少该路由时必须停止,不能回退 /api/HDFS/DeleteObject。 此操作只允许当前租户仍然有效的平台超级管理员 DiyToken 会话;访问密钥、普通帐号、撤权用户、未知绝对路径、URL、通配符、路径穿越和租户根均被拒绝。平台用当前租户配置固定供应商与桶,完整核验原始对象列表和零字节大小,只删除精确标记 key;不调用前缀批量删除。非空目录、缺失大小、未知分页、权限失败和删除后无法确认空前缀都返回失败。标记已不存在时也需完整回读才返回幂等成功,成功正文须含 DeletionMode=EmptyDirectoryMarkerOnly 与 VerifiedAbsent=true。 标准 MCP microi_delete_empty_directory_marker 默认仅产生本地 dry run;执行参数 confirmExecution 必须精确等于 filePathName。删除结果未知时保留原 key,先回读,不能自动换键或再次上传。 该能力不是“零字节条件删除 CAS”,也不证明历史从未 PUT。供应商 SDK 没有统一的条件 DELETE;若同 key 在核验后被其它写入替换,无法保证只删除旧版本。因此执行前必须隔离原业务请求、排除活跃写入,并单独保留历史 NoPUT 或受控恢复的证据。并发新写入的后代不会被删除,但会使删除后回读失败。 #### 升级兼容说明 - 历史公有文件不会因升级自动迁移;先复制到租户私有桶、回读核验,再停止旧公有访问。数据库继续保存租户内相对路径,不保存对象存储密钥或真实签名 URL。 - 旧页面只传 FilePathName 获取私有地址,升级后普通帐号会失败。应补齐 FormEngineKey、FormDataId、FieldId、SysMenuId,不要把接口改回匿名或给普通角色开放文件管理权限。 - 旧版标准表单无需修改字段值;升级 PC 前端和 API 后,上传组件会自动补齐字段上下文,后端继续以原字段“禁止匿名访问”配置为准。若只升级 API 而仍使用旧前端,缺少字段上下文且没有管理员目录授权的普通上传会安全降级到私有桶。 - 旧自定义上传若只传 Limit=false 和多级 Path,可在普通系统设置中按角色配置 HdfsUploadRules,无需逐个修改客户端;新代码仍优先补齐可验证的表单字段上下文。无论哪种方式,都不能只把客户端参数当作公开授权。 - 上线前至少用“普通用户 + 超级管理员 × 公有字段 + 私有字段 × 新增 + 编辑”验证实际对象桶、返回 Limit 和读取方式,并覆盖越权、配额耗尽、Redis 故障和多节点并发;不能仅以“上传成功”作为安全验收。 - Upgrade16 的六个字段全部可空,空值继续使用平台默认/硬上限,因此老租户升级不会被强制停用上传。升级完成后应回读字段元数据和租户配置,并刷新 SaaS 运行缓存。 完整平台保护表、登录会话、CORS、SSRF 和升级基线见 平台安全与兼容基线 (https://microi.net/doc/more/security.html)。 --- ## ⚙️ 步骤一:指定存储方式 在 【系统设置】→【开发配置】 中指定存储方式。系统设置由表单引擎驱动,可在表单设计中自由扩展更多自定义存储方式。 [存储方式配置: https://file.microi.net/upload/img/csdn/5f7e4c8a6b824c51b1c50de50827abdd.png#pic_center] --- ## ☁️ 阿里云 OSS + CDN 在 【SaaS 引擎】→【Aliyun】 处配置相关参数: ### 上传提示 OSS 配置不完整 OBJECT_STORAGE_CONFIG_INCOMPLETE 表示本次操作需要的 OSS 配置缺失, 不代表必须购买阿里云存储。先核对实际生效租户的 HDFS:平台支持 Aliyun、MinIO、S3;旧库未设置时兼容默认走 Aliyun。 若实际使用 MinIO/S3,应在 SaaS 引擎选择对应方式并补齐该方式配置。 OSS 上传按本次实际访问范围检查配置: 操作 | 所需配置 | 普通公有文件或 JS/CSS 编译资源上传 | AliOssPublicBucketName/Endpoint/AccessKeyId/AccessKeySecret | 私有文件或应用源码上传 | AliOssPrivateBucketName/Endpoint/AccessKeyId/AccessKeySecret | 从私有原图生成公有预览 | 私有桶读取配置和公有桶写入配置 | 表中的 / 表示同一前缀下的四个独立字段,例如 AliOssPublicEndpoint。 包含按桶校验修复的后端仅检查本次所需配置;旧后端可能同时报出两套缺项。 完整应用交付同时涉及私有源码和公有编译产物,仍应配置两类存储。 公有资源还需核对 FileServer/CDN 地址和读取权限;升级不能代替真实存储配置。 保存后通过 SaaS 引擎刷新租户运行缓存,再重试并回读文件字节、哈希、MIME 及应用发布结果。错误中的 StorageScope 和“缺失字段”可定位本次配置范围; 密钥只在 SaaS 后端配置,不发到聊天、前端或 Git,也不要复制其它租户的密钥。 [阿里云OSS配置: https://file.microi.net/upload/img/csdn/dd353af2971c4057b3d47c1f3ad9d81c.png#pic_center] --- ## 📦 MinIO 在 【SaaS 引擎】→【MinIO】 处配置相关参数: ### 按下载流量自动分流到 NAS 公开模型、图片、视频、安装包和应用运行文件反复下载时,可以保留 OSS 作为主存储, 只把达到下载流量阈值的资源复制到 NAS 的 MinIO。后台验证副本后,已接入的入口选择 FileServerBak,减少后续 OSS 公网流出和 CDN 下行。无需全桶搬家。 #### 配置在哪里 打开 系统设置(后台路由 /system-config),查找以下字段。备用地址在 开发配置;阈值和统计窗口在部分旧表单布局中位于默认 Tab,可按字段标签查找。 字段标签 | 配置名 | 示例与含义 | 备用文件服务器 | FileServerBak | https://files-backup.example.com:9443;当前热点运行时要求 HTTPS origin,不带桶路径 | 备用分流阈值(GiB) | FileServerBakThresholdGiB | 1;实际下载字节达到 1 GiB,不是文件大小达到 1 GiB | 备用分流统计窗口(小时) | FileServerBakWindowHours | 24;使用已结束的完整北京时间自然日,不是滚动 24 小时 | 大文件分流阈值(MiB) | FileServerBakSizeMiB | 100;单文件严格超过 104857600 字节即进入自动复制,无需等待流量;留空或 0 关闭新的大小晋级 | 大文件条件需要实际后端返回 Capabilities.SizePolicyProtocol=PublicResourceSizeQualification/1, 不能仅凭字段存在判断已经运行。它与下载量条件为“或”关系:一个文件超过大小阈值, 或资源/完整依赖组达到完整日流量阈值,均可进入原调度器复制。 应用按当前可信发布清单整组处理,不能把许多小文件的总大小当成一个大文件。 复制及公开权限、版本、原始 SHA、MIME、CORS、Range 校验通过后才选备用地址; 首次可信晋级保存独立的大小资格,不伪造下载流量。后续调高大小阈值或流量下降, 不撤回已保存的同源偏好;显式停用、源权限/版本与 NAS 健康门禁仍优先。 原生调度每轮最多列举当前 SaaS 公有桶的 200 条对象元数据、排队 5 个候选, 读取与排队预算 25 秒,签名主库游标跨轮恢复,完整元数据扫描后 6 小时再扫描。 这是元数据发现与选择性复制,不是全桶正文迁移。新增或调低阈值重启元数据游标, 不会重下已校验副本。原生单对象/组 2 GiB、128 个依赖的限制仍有效; 超限对象会记排除计数,私有对象、缺少可信清单的独立脚本不参与匿名分流。 可在 Status.SourceObservation.LargeObjectInventory 查看扫描游标、到期时间和排除原因; Ready 与官网/应用的实际请求地址仍须分别验收。 在 SaaS 引擎 → MinIO 保存该租户对应的端点、公有桶、私有桶和后端凭据, 并核对端点与备用 origin 的绑定;原 FileServer 和 HDFS=Aliyun 保留。 公有、私有桶分别配置,填写备用地址不会把私有文件变成公有。 达标后会一直走备用服务器吗? 首次可信达标后持续保留同源资源的 NAS 优先偏好,后续流量下降不会自动切回。 内容换版需重新核验副本,权限或来源改变、管理员停用、NAS 不健康时仍服从对应门禁; 可用性回退不等于取消已保存的偏好。 #### 自动处理与验收 - 升级兼容后端及配套 系统设置、系统日志/监控 应用;先回读运行能力,不能只填字段。 - 在 任务调度 核对固定任务 microiPublicResourceOffloadScheduler, 由 mci-file-resource-scheduler 执行;采集、复制、校验和续期使用原生共享租约。 - 在 系统日志/监控 → 资源 查看采集日期、达标资格、复制进度、失败原因和 Ready。 CDN 日报可能迟到;缺报表或没有观测表示未知,不能解释为零下载。 - 逐个访问实际官网、应用和下载入口,确认最终请求到备用 origin, 并验证字节、MIME、Range、CORS 及私有拒绝。复制完成不等于消费者已接入。 - 对比服务正常、代码和覆盖范围明确的完整自然日账单,分别核对 OSS 公网流出、 OSS 向 CDN 回源流出和 CDN 下行;当天部分账单不能证明每日节省金额。 应用的 JS/CSS 按可信发布清单整组复制,模型连同贴图等依赖校验。 平台启动前加载的主壳 JS、硬编码 OSS/CDN URL、未接入的 loader 和私有对象, 不会因填写 FileServerBak 自动分流。 这些入口需要单独接入与验收。 首次复制仍会产生一次源站读取费用,NAS 的公网容量取决于上行、稳定性和并发能力。 #### 让 AI / MCP 完成配置与排查 可向已连接当前租户的 AI 这样描述: 请检查当前租户的热点资源分流能力,按 1 GiB / 完整北京时间自然日配置备用文件服务器。 核对 SaaS MinIO、固定调度任务、复制校验和实际请求地址;达标后保持 NAS 优先。 保留 OSS 源文件和私有权限,分别报告请求下降与完整日账单变化。 AI 先读取文件上传与系统可观测性 Skills,通过 microi_run_engine 调用受管 mci-file-resource-runtime 的 Capabilities、Status;阈值使用当前 sys_config 的标准配置保存与回读,不能直接修改签名状态或 Ready。 MinIO 凭据通过专用 microi_manage_saas_minio 的 Inspect → Save → Probe 维护, Save 绑定刚读取的 Revision;工具不改变 HDFS/OSS,也不代替资源复制。 如当前 MCP 的 list_tools / describe_tool 找不到此工具,先更新并重新加载 MCP, 不能转用通用表、临时 V8 或明文配置绕过密钥保护。 下文保留各协议的接入范围、权限与恢复规则,升级时按实际能力逐项核对。 ### OSS 副本与备用文件地址 将 OSS 复制到 NAS MinIO 时,保留公有桶、私有桶及对象键的对应关系,通过对象存储 API 复制;不要直接把文件写入 MinIO 的数据卷。先复制、核对对象清单和可用摘要,再补齐期间新增或修改的文件,验证后才切换读取入口。复制本身会产生一次性的 OSS 公网流出,应与日常访问费用分开统计。 历史功能把 sys_config.FileServerBak 作为可含桶路径的备用公有文件前缀;当前热点运行时要求不带桶路径的 HTTPS origin,桶由 SaaS MinIO 配置绑定。它只供明确接入备用路由的功能读取;新增或填写字段不会自动搬迁文件、改写已有绝对 URL,也不会改变后台 HDFS SDK 的存储提供商。原 FileServer、后台读取来源及具体应用的切换必须分别验收。 热点分流按明确资源维护目录,不要求全桶同步。FileServerBakThresholdGiB 表示一个资源或完整公开运行依赖组的实际下载量阈值(1 GiB=1073741824 字节),FileServerBakWindowHours 当前使用 24 小时;通用阈值默认空。只有包含 PublicResourceOffload/2 的后端与系统日志应用都已部署,采集、复制和入口选择才可运行;旧版本填写字段或登记目录不会自动分流。 第二版运行时要求 FileServerBak 是 HTTPS origin(可带端口,不带桶路径),SaaS 引擎的 MinIO 内外地址与之相同,并分别配置公有桶、私有桶和后端凭据;主 HDFS=Aliyun 与原 FileServer 保留。后台读取阿里云已订阅的 CDN 运营报表,并累计已接入 HDFS 公有流式读取的实际 OSS 响应字节。按已结束的北京时间自然日合并互不重复的正向下界,达到阈值即可晋级;报表 TOP 榜不是全对象全集,未接入路径、缺报表和丢失观测仍是未知,不以空集合证明零流量。首次迁移和校验读取排除在自动晋级流量之外。 采集器每轮只补采最近两个已结束的北京时间自然日,以补齐迟到报表而不扫描历史全量。同日、同域、同精确对象键重复出现的累计快照取最大值,不重复累计;跨域观察分别保留,不跨天合并流量阈值。每个日期开始 SDK 或云端读取前先持久化指向另一日的签名恢复游标,避免某一天持续耗尽预算而饿死另一日。缺报表、未覆盖源或过期观察是未知,不能解释为零。状态仅保存最多两条可空日期摘要;Pending/Failed 的计数保持空值,诊断明细超过 3 MiB 时显式标记截断,完整原始状态仍受签名预算约束。一次可信达标后的同源持续 NAS 偏好不因低流量撤回;停用、权限、版本和健康门禁仍优先。 包含原生 CDN 别名采集扩展的后端,才可把当前 FileServer 之外的订阅域纳入自动归属:固定云只读接口必须重新确认唯一在线 OSS 源与当前 SaaS 公有桶/端点一致,并且只有显式同源 Host 规则,无路径改写、条件、多源、边缘函数、未知字段或截断配置。映射使用主库签名 CAS 与独立 ObservedSince,Pending、失败、来源/规则指纹变化或观察间隔超过 24 小时均重建起点;只采用本轮重新验证成功且覆盖完整自然日的证据,不借用旧存储绑定起点、不回填历史报表资格。游标和映射只存入既有 source-observation-v1 签名行的 CdnAliasCollector 子结构(最多 50 条、128 KiB),不增加旧节点不认识的物理 StateKey;同源同目标配置保留,来源/目标变化或旧节点覆盖丢失子结构时保守重新观察,不追认历史窗口。每轮最多公平探测两个别名,探测前先保存游标,每个元数据调用最多 15 秒且仍受宿主总预算;既有主 FileServer 路径和 SDK 资格独立保留。LastCollection.AliasCollection / CdnAliasAuthority 只报告已探测和该日可归属范围,未覆盖不是零。对象仍逐一检查当前有效 ACL,公有桶名称不能授权私有对象;低流量不撤回已验证同源长期偏好。此扩展不改变 SourceMappings 或任何旧硬编码下载地址;原生资格、复制 Ready 与实际消费者分流必须分别验收。普通 V8/MCP 不接收调用方域名映射、Functions、流量、SHA 或凭据。 实际旧地址消费还需要 PublicSourceUrlRoutes/1:现有 public-url 空 ObjectKeys 回读可附 AliasCandidates,但它只是已登记别名的发现信息,不是 SourceMappings 授权。明确公有的异步客户端只把候选域的精确 HTTPS、无查询串/签名地址通过 PublicUrls 提交同一受管接口;原生必须复核当前源/目标绑定、固定云元数据与无改写映射,再沿当前公开 ACL、完整依赖组、Ready 和健康租约选 NAS。进程内证明按租户/源/目标/域/指纹隔离,最多 512 项、4 个元数据并发、每次 4 秒预算,成功与失败同身份最多每 60 秒一次;客户端只接受精确 SourceUrl、ObjectKey、目标受管前缀及不超过 60 秒且不长于 NAS 健康期的回执。上下文变化、迟到响应、异常行、过期与 NAS 失败回原地址,不改变保存的资源键;旧后端未返回能力时保持兼容。原有 GetPublicResourceRoute 上传/列表/卡片链可复用该能力,但同步 assetUrl、页面启动脚本、旧 HTML 与任意 Three.js URL 不会因此自动接入;通用 SDK 和具体组件需独立等待异步选择后再赋 src/load。应用整组的稳定 V3 入口和 Generic 公有对象是不同通路,不能用本能力绕过私有应用/对象权限。 晋级后从当前已发布公开应用、同版本完整清单及 V3 已提交清单摘要取得来源,按整个 JS/CSS/模型依赖组复制到 MinIO 独立版本前缀。源文件流式校验大小与 SHA-256,上传后签名回读,再做匿名 HTTPS、MIME、Range、CORS 和摘要验证;仅授权该已验组的 GetObject,不授权列桶、写入或私有桶。共享签名状态、租约栅栏和逐文件检查点支持跨节点恢复;上传结果未知时先读已有目标,异内容目标拒绝覆盖。目录元数据、调用方传入的 Ready 或字节数不能授权切流。 历史安装微服务的商城 IsPublic=0 不等同于其已发布编译文件是私有对象。只有原生 InstalledPublicBuild/1 提供者从主库独立读取当前应用、唯一协议 0–2 历史版本、唯一启用服务及完整 dist/ 文件集合,复用安装别名四方一致性校验,并确认匹配且未到期的 InstalledAliasReconciliation/1 三次稳定检查点后,才可把整个公开构建依赖组纳入分流。每个文件必须标记 PublicBuildOnly 或 PrivateSource+PublicBuild,源对象当前公有 ACL、版本、原始 SHA 与同一复制 Run 的目标回执都要核验;单个 JS、私有源码、陈旧检查点、缺失成员或跨租户路径均不准入。首次开放目标前和健康续期复查全组,实际选择时再复查所选源对象;失败继续原授权下载链,不把 IsPublic、LegacyUnverified 或协议号改成公开/V3 成功。该接口只供原生宿主内部调用,不开放新的 HTTP、MCP 参数或可编辑 V8 能力。探测链最后一个 NAS 响应返回后,准备与续期都通过同一个完成门重新读取当前整组权威和公有 ACL,再检查租约并提交 Ready 或健康时间;变化则走现有失败撤权。两种真实访问入口使用同一个 5 秒总预算覆盖绑定、Redis、主库权威、ACL 和 HTML HEAD,原生权威读取最多 4 个并发,超时停止等待但占用槽位直到已启动读取结束,后续 SQL 不再启动;后台任务仍使用宿主原有取消期限。 稳定微应用资产入口的原生叶资源兼容(须部署包含此修复的后端):既有 Controller 完成当前版本/清单选择后,若应用组路由不可用,可只读查询该清单精确对象键、原始 SHA-256 与大小匹配的通用公开 Ready 副本。公开对象仍须通过源 ACL/版本、源与目标绑定、健康期和受管 NAS URL 校验;不会把应用 IsPublic=0 改为公开,也不公开私有源码或私有对象。HTML、JS、CSS、JSON、SVG 及 OriginalStorage=1 保留原链。root/latest/版本路径即使同名同 SHA 也不能互借 Ready;历史榜单对象已复制不等于当前版本路径已接通。直接 static/CDN URL 绕过稳定入口时仍不受本兼容影响。该能力不新增 V8/MCP 动作或可由调用方覆盖的 owner/摘要参数。 当前权威公开清单中的 Sha256 / ContentHash 与历史 Hash(含小写别名)统一按原始文件 SHA-256 校验,仅接受 64 位十六进制值;现代字段优先,任一非空别名无效或多个别名冲突均拒绝。此兼容不改变文件大小、完整原始字节 SHA 回读、公私权限或发布/检查点状态,不能把 ETag、MD5 或目录声明当作可信摘要。 Web、UniApp 按当前 ApplicationType 选择已提交 V3 版本的权威清单,残留甚至多条旧 MicroService 记录不能抢先遮蔽。主库 AppKey 的原始大小写用于不可变 OSS 前缀;真正 MicroService 保留原唯一启用服务及历史 AppId 查找兼容。共享分组索引按规范化状态键去重,保留存量成员,不重复展示或重复健康检查。后端按当前应用、版本与请求指纹核对完整清单摘要;私有、未完成发布、跨应用版本和越界路径均拒绝。 标准微服务资源入口在读取 OSS 正文之前选择副本;商城打开公开体验时通过 mci-file-resource-public-url 只读选择已验证 NAS 入口,原发布指针和清单保持。入口跳转前执行有界健康探测,副本失效、配置/版本/摘要变化时继续原下载路径。首次可信达标后持久保留该资源的 NAS 优先偏好,后续日期低流量、统计窗口过期、进程重启或 Redis 缓存丢失均不取消。新内容版本仍保留偏好,但必须重新校验当前公开权限与完整副本后才能路由;显式停用优先于采集,不会被自动启用。管理员在系统日志的资源页查看运行状态、指定历史热点及暂停/恢复;历史指定单独留因,不冒充本日阈值已通过。未接入的旧绝对 URL、平台主壳在配置前加载的 JS 和非受管静态文件不会凭字段自动改址,必须逐条补齐消费路径与验收。 首次进入自动分流必须有足以证明阈值的新鲜可信流量下界(或单独留因的历史人工指定);以后读取已签名持久化的首次资格。无论是否已达标,当前版本公开权限、与源大小和 SHA256 一致的备用副本、正确的 HTTPS 和访问权限仍须全部通过,已接入消费者才可采用备用地址。流量未知或过期不能按 0 处理,历史榜单仅用于选择候选。JS/CSS、贴图和模型依赖须显式成组;平台主壳 JS 在系统配置装载前请求,微服务还有独立代理链,二者不能仅靠修改通用文件 URL 函数覆盖。私有源码不纳入公开资源目录,跨租户对象也不能凭当前租户管理员身份自动纳入。 仅修改应用私有源码会将 BuildStatus 置为 Changed,不必然改变当前已提交的公开运行版本。包含本修复的原生后端仅在应用与版本均为 V3 Completed、当前提交 Id/应用归属/正栅栏一致、完整清单重算 SHA 与双方摘要一致、租户/AppKey/版本/请求前缀匹配时继续使用已验证 NAS 副本;微服务当前投影还须与该提交的 Id、栅栏、请求指纹、摘要及每个不可变对象键一致。Legacy、无提交证明以及 Failed/Building 等状态不放宽。仅草稿状态变化不改变 SourceRevision,因此正常 Ready 健康检查不应重新读取 OSS 正文;真实权限、版本、清单或源绑定变化仍停止旧路由并按原流程撤权。不要手工把 BuildStatus 改回 Success 或把目录改为 Ready;策略及匿名完整性验证仍须通过。该行为需要包含本修复的 API 原生产物上线,仅发布文档、页面或 Managed 引擎不生效。 升级时旧 v2 应用签名没有 OSS 桶/端点,既有 SourceRevision 不足以反推来源。缺少 ConfiguredSourceBinding 的旧组保留副本与签名记录,但拒绝自动继承长期偏好及实际路由;管理员以现有 PinHistoricalHotspot 明确重申当前来源后,只有完整清单、版本、原始 SHA/大小与目标配置一致的旧 Ready 才可保留 RunId/目标前缀,健康回读通过后恢复路由。已有来源绑定不一致时绝不套用此复用分支,须新资格或明确新选择及新运行。新 CDN 资格只使用当前绑定首次观察之后的完整日,旧未绑定 SDK 累计保留但不合并,缺失不能解释成零。 升级先保留旧 Redis/分组索引并安装主库事实表,再统一升级所有官方工作节点。新宿主在调度接管早退以前,以 tick/storage 租约把索引内已签名的应用/对象状态(包括 Disabled)及已有 handoff 原样迁移主库;不新增 Pin、Ready 或推定来源。只有全部元数据写入确认后才记录迁移完成,暂停/删除任务不会因此运行。损坏签名、超限、取消或保存未确认均失败关闭。此迁移不能还原升级前已丢失的 Redis 状态,完成后也不得运行会继续写旧 Redis 协议的旧节点或回滚旧运行时。 副本验证与 OSS 权威校验保持分开:后台安装应用别名检查必须继续验证其权威存储,不能用 NAS 的正确副本掩盖 OSS 别名错误。首次精选复制、后续 NAS 分流以及检查点减少重复读取的效果分别计量;没有完整可比账单时不声明实际节省金额。 私有文件继续由服务端校验业务权限后签名,不能将 OSS 的签名地址直接替换域名,也不能为了备用访问把私有桶设为匿名。验收应包含严格 TLS、Content-Type、CORS、HEAD/Range、大文件下载及私有访问拒绝/授权成功。家庭宽带的下行标称带宽不代表公网服务的上行能力,正式分流前需要测量上行与并发访问。 服务端复制对象会保留源文件和原 MIME、元数据。旧后端的 MinIO SDK 7 对带 charset 的 MIME 可能构造重复 Content-Type,造成复制签名不匹配;升级后端复制传输修复即可,无需修改租户密钥、源对象 MIME 或关闭签名校验。恢复时保留原请求键,先回读目标字节数和 SHA-256,再继续;公有应用还需独立验证 CDN 字节。复制取消或超时后不要直接重试,目标可能已写入。 #### 精确历史安装包:独立版本副本 历史安装包不能把下载链接替换为 latest,也不能通过提高通用对象字节限额绕过历史版本的权威清单与权限检查。历史下载候选使用四个精确字段 SourceTenant / AppKey / VersionId / AssetPath,由当前主库重新读取公开 Web/UniApp 应用及该应用已完成的协议 v3 历史版本,重算完整 manifest SHA,并派生不可变 ReleasePrefix。文件名里的版本文字不等于发布版本号;以历史版本 Id、请求指纹、完整清单和该文件原始 SHA/大小为准。当前应用撤稿、私有或历史版本失效时拒绝;新版本尚在构建不改变既有已提交历史版本。 管理员通过标准 MCP 调用 Managed mci-file-resource-runtime 的 PinHistoricalAsset,只提交上述四项身份;ListHistoricalAssets 只读查看,DisableHistoricalAsset / EnableHistoricalAsset 保留明确暂停/恢复意图。首次 Pin 是单独留因的人工历史选择,不伪造当日 1 GiB 阈值。HTTP/MCP 仅登记意图,不能接收 URL、桶、SHA、Ready 或复制回执。历史状态以 archive:<身份摘要> 存入已有主库签名/CAS 表,Redis 仅索引;重复 Pin 不自动解除暂停。 此独立链路当前仅接受已提交清单内的 .exe / .dmg / .zip 单文件,最高 2 GiB;它不是全桶迁移或任意 URL 下载器。后台复用原生 8 MiB 分块传输,每个已确认块及可恢复整文件 SHA 状态持久化后才能推进;先核对已有 NAS 目标,未知 PUT/完成结果先对账。NAS 服务端 multipart 组合后必须再按分块回读并核对权威整文件 SHA,匿名分块逐段验证 SHA、MIME、Range、CORS 和稳定 ETag,只有全部确认才 Ready。每轮仍受宿主四分钟预算与每步有界超时;中断仅续未确认步骤,不能把预算耗尽记作完成。源/目标变更保留旧公开授权账本并先撤权,旧私有分块/上传所有权记录也保留;待清理记录达到边界时停止新运行,不扫描或删除未知对象。 公开消费入口为 Managed HTTP mci-file-resource-archive-download,只读重验上述精确历史身份后返回 302 + Cache-Control: no-store:Ready 且当前绑定/权限/健康有效时采用 NAS,否则返回该历史版本原云地址;权限不再公开或权威读取失败时拒绝,不降级到调用方提供的链接。该请求不复制文件、不代理安装包正文。官网只把已独立核对的历史记录映射到此入口;其它未核对历史链接保留原地址。先部署原生能力和受管接口,再发布消费者并验收,不能先发布尚不存在的入口。发布证据、真实复制/下载路径及完整日节省仍分开报告。 #### 通用公开对象 v3:登记、复制与实际消费 以下是通用对象候选的协议和接入范围,须以对应后端、应用包和客户端的实际部署回执为准;本文不代表已上线,也不代表费用已经下降。应用运行清单的第二版分组复制继续独立工作。应用组、通用对象和调度接管由当前主租户主库的 mci_file_offload_state 保存带签名及 Revision CAS 的权威状态,Redis 只作可恢复索引;系统日志资源页只是可回读的投影;普通 CRUD、前端传入的 URL、Ready、摘要或流量不能授权复制或公开。 - 初始业务阈值可设为 1 GiB / 24 小时;实际采集窗口是最新已结束的完整北京时间自然日,不是当前时刻向前滚动 24 小时,也不是当天部分数据。合并来源域名已绑定的 CDN 报表与已接入直读 SDK 的实际响应字节,作为不重复的正向下界;达到阈值足以选择候选,榜单未出现、没有观测或低于阈值不能证明全路径流量低或为零。对象键在该日是否始终对应同一内容版本仍未知;复制时重新冻结当前源版本,不能把历史键流量说成新版本的精确流量。 - 首次达标签名保留原完整自然日证据及当时阈值,永久偏好不把旧证据改写成今天的流量。旧签名 Ready 仅在完整 86400 秒窗口、阈值与源绑定相符(或已明确留因的历史选择)时迁移偏好;普通表行、任意历史状态和过期缓存不能生成授权。源桶/端点变化不继承旧绑定的资格;内容更新仍须重新复制验证。 - 从 8.7.7 原生能力及配套系统日志 Managed 包开始,通用对象支持 PinHistoricalPublicObject(仅 ObjectKey):当前主租户管理员明确指定历史公开热点,原生 HEAD/ACL 后保存带源绑定及版本的选择;worker 再次匹配后才取得长期偏好并复制校验。人工选择不生成 24 小时流量,首次入队不是 Ready;同一来源后续低流量不撤回,内容变化重验,新来源必须重新明确选择。已停用资源保持停用,需单独 EnablePublicObject。只允许当前 SaaS 公开 OSS 的精确键,不接受 URL、调用方摘要、凭据或私有来源。 - 当前通用对象 worker 支持明确公开的 PNG/JPEG/WebP/GIF/ICO、MP4/WebM、MP3/WAV/OGG、ZIP/BIN、JSON、模型贴图/字体等白名单叶子资源,以及 GLB/glTF 及其可解析的同桶相对依赖。8.7.7 原通用路径单对象最多 64 MiB、闭包最多 256 MiB / 128 个对象。部署具备 Capabilities.GenericLargeCopyProtocol=PublicResourcePinnedByteCapture/1 的后续后端后,单对象及依赖闭包分别最多 2 GiB / 2 GiB,仍最多 128 个对象(包含入口);超限保留未完成/拒绝状态,不能截断后标记 Ready。独立 JS/CSS、HTML 等需要已提交且可信的依赖清单;该通用路径尚未接入这类清单,返回依赖清单缺失,不能把脚本当无依赖文件。已有已发布应用的 JS/CSS 继续走完整应用清单分组路径。 - 源对象必须属于当前 SaaS 绑定的阿里云公有桶,并由对象 ACL(或继承的桶 ACL)和匿名严格 TLS HEAD 共同确认公开。每个对象冻结 ETag、VersionId、大小、MIME 等版本指纹,条件 Range 读取继续验证版本;ETag 是读取条件,不是 SHA-256。GLB/glTF 依赖从 NAS 私有暂存重新读取解析,不接受任意远程或签名 URL、跨桶、穿越路径或前端声明的授权事实。源权限、版本、完整依赖和当前目标绑定必须保持一致;路由签发也重读源权威,不能只相信 NAS 副本正常。 - GLB/glTF 的 EXT_texture_webp 需要包含该解析修复的原生后端:textures[i].extensions.EXT_texture_webp.source 必须是指向本清单 images 的非负整数索引,越界、缺失或类型错误拒绝。内嵌 WebP 不增加外部对象;外链 WebP 与普通回退图片仍进入同一有界 URI 闭包,逐对象核验源 ACL、版本和摘要。未知扩展继续拒绝;该兼容不授权私有源、不开放独立 JS,也不自动改写已有模型加载地址。 - JSON(含商城完整安装包)只作为原样字节叶文件:不解析、重写包内 URL,不据其中内容扩大依赖或公开范围,原 64 MiB 限额或新版能力回读的 2 GiB 限额均以实际后端为准;这不提高商城包解析/安装接口自身的正文上限。商城消费方须将成功路由的 VerifiedSha256 / VerifiedSize 与当前权威精确版本的 PackageSha256 / PackageSize 比对,再校验同响应 Binding 的 origin、公有桶、配置摘要及有效期。SourceRevision 是运行清单摘要,不能代替包的原始 SHA。旧后端缺少证明、对象未 Ready 或任何不一致均回原地址;不能把最新版本替代用户选定的历史版本。 - 源按 8 MiB 分块进入 NAS 私有暂存,每个已确认块有签名 SHA-256 回执;恢复时核对暂存块并复用,避免重新下载已确认的源块。不超过 64 MiB 的原路径由已验证 NAS 暂存计算原始 SHA。新版 64 MiB–2 GiB 路径从条件固定的源字节逐块写入、回读并推进可恢复 SHA-256;在确定依赖闭包和目标前缀后,将同一批私有块交给共享 NAS multipart 原子,不为计算摘要再次读取 OSS。GLB 只从已核验私有块提取头及至多 4 MiB JSON,跳过大 binary 段;不建立大文件本地临时副本。目标完整 SHA 与匿名字节均按条件 Range 保存进度后确认。结果未知先对账,不能盲目重传。只有完整依赖均通过大小、SHA、MIME、严格 TLS、HEAD/Range 与公共 CORS 验证才 Ready;未包含精确 Origin 修复的通用后端只接受 CORS 为 *;包含本修复后亦接受与本次配置 ApiBase 的 HTTPS Origin 完全相同的单一值,错误域名、多个值、null 或缺失值仍拒绝,不携带 Cookie。暂存清理和预算耗尽分别记录,不能伪装复制完成。 - 公开授权之前,原生签名状态的 PublishedRuns 账本先保存该次可能公开的前缀及目标绑定,覆盖响应未知和进程中断。停用、源转私有、源版本或目标绑定变化时停止签发新 NAS URL,后台工作器再对账本中的旧前缀执行限定撤权:有效租约下移除本功能拥有的 GetObject 授权、回读策略,再逐对象匿名 HEAD 确认 403/404。其它授权策略保留;移除自有策略不等于匿名已禁止,撤权完成前不得声称旧链接已经失效。旧目标绑定无法确认时记录 PreviousTargetBindingUnavailable 并阻断,不使用新目标的凭据冒充旧目标撤权成功。已被浏览器或第三方缓存的历史字节不能召回。 已提交公开应用的大文件复制使用共享的 PublicResourceLargeAssetTransfer/1 原生状态机:在 2 GiB 内按 8 MiB 私有分块、NAS 服务器端 multipart Copy、目标条件 Range 与可恢复的完整原始 SHA-256 处理。应用和精确历史版本路径各自保管签名资格及权威清单,传输原子不授予公开权限、不直写 Ready。每步最多 120 秒且同时服从任务预算与租约取消;已确认块保留来源、大小及 SHA,未知 PUT/合并先对账,不重新下载已核验 OSS 块。已有完整目标优先在 NAS 分段核对原始 SHA,不因升级重复源下载。应用单文件超过 2 GiB 暂保留原流式路径,不宣称其已获得此断点能力;通用对象只有回读到 PublicResourcePinnedByteCapture/1 后才走未知摘要的原生分块捕获支路;当前应用和历史版本仍要求权威清单预知 SHA,两种入口共享传输而不混淆授权。 最终匿名验收同样按条件 Range 逐块保存进度,只有整文件原始 SHA 与清单匹配、每段公开字节及 MIME/CORS/Range 均正确后才 Ready;ETag 仅绑定读取,不能代替 SHA。取消重试复用已验证段,目标 ETag 改变使匿名段证明失效。旧副本 MIME 错误须先核对完整原始 SHA,再仅对该受管对象执行条件服务端 Copy 修正 Content-Type、保留其他元数据,并重新按段核对完整 SHA;不能放宽 HTML/JS/模型类型断言。完成后按签名归属删除本次私有暂存;停用、撤权或内容换版则先持久化退休意图并撤公开授权,再按精确 uploadId / chunk 清理或保留待续的拥有账本,不清理未知对象。临时网络或未知写入失败只撤可用性,保留已确认分块和 SHA 状态;清理中断后仍能继续,重新启用必须建立经当前源验证的新运行。新限额没有扩大流量采集域、直读 SDK 覆盖或浏览器消费者范围,未进入可信观测的对象、私有资源和绕过解析器的硬编码 URL 仍不能宣称已分流。 固定任务 microiPublicResourceOffloadScheduler / mci-file-resource-scheduler 由任务调度原生分派并回读当前主库任务状态,不能用可编辑 V8 伪造宿主身份。任务与旧 HostedService 共用 tick 租约,管理员标准保存确认持久化后即在同一租约写入签名 handoff(已暂停任务也接管但不运行);表单事务 RuntimeOnly 路径仍由首次合法原生 tick 接管,handoff 同样进入主库签名状态,缓存丢失不恢复旧宿主;未确认接管时先回读部分保存结果;接管后暂停、删除、改名、禁用任务不会让旧宿主自动恢复。采集另用共享租约和 30 分钟到期门禁,人工 Collect 同样受限。每轮 4 分钟预算用尽返回 BudgetReached / Partial:true / AllResourcesReady:false,下轮从已确认状态恢复;它不是所有资源完成,也不承诺任意大小组在一轮内复制完。详见 Job Skill 的“公开热点分流的固定调度入口”。 上线前先通过拥有方应用包安装并回读主库状态表及唯一 (Owner, StateKey) 索引,再切入新版后端;表不存在或主库不可用时失败关闭,不退回只信 Redis。状态行由原生签名/CAS 写入;该精确表属于平台管理员专属表,普通角色即使误授表或菜单 CRUD 也不能读取、回放旧回执或写入。已有可信平台管理员、受信后端/V8 及直接数据库维护仍属于平台信任边界,签名不能防止有权维护主库者执行数据库回滚。恢复索引先以数据库方言分页读取最多 2001 个轻量身份;超过 2000 条拒绝并保留原状态,随后逐行核对 Revision,回执累计最多 32 MiB,超限或并发变化整批拒绝;取消后不再发起下一条读取,已开始的命令仍受自身 5 秒超时。需要扩容时必须另行实现分页恢复,不能把拒绝当完成。 4 分钟是调度执行预算,不是所有远端 I/O 的绝对终止时刻:主库单条命令 5 秒,索引恢复可能有多条同步读取,外层取消不能保证驱动在同一时刻结束;阿里云同步 SDK 请求使用自身 20 秒超时,取消后可能仍有在途请求尾部。MinIO 策略 SET 不提供远端原子 CAS 或租约 fencing,写入前后验租、策略比对和未知结果回读不能消除与外部策略写入者的竞态;结果未知时不盲重写,也不能承诺外部副作用“恰好一次”。 对于已经复制校验、仍有高流量请求的历史硬编码下载地址,还须接通旧地址的消费入口。系统日志包 v8.6.25 对既有 static.itdos.com、file.microi.net 上 Windows、Mac x64、Mac arm64 三个 Chromium 137.0.7151.68 安装包提供精确、可回读的重定向;这不是全目录或任意 URL 替换。两个入口同时接入后,NAS 不可用时只回退到逐个验证过的固定公共 OSS 源,保留对象原权限,避免回到入口造成循环。上线验收须检查完整跳转链、NAS 文件大小与 Range、原有规则未变;达到阈值后的偏好持续保留,暂时故障回源不等于取消偏好。实际流量下降与完整自然日账单仍须另验。 #### 已接入的客户端入口与明确未覆盖项 包含 8.7.11 修复的原生后端将 5–64 MiB 既有公开对象统一交给已签名暂存块的原生 multipart 完成协议;小于 8 MiB、只有一个捕获块的对象也适用。它先对账已存在的 NAS 目标,再校验私有块、完成精确分片并重新核对完整 SHA、匿名 MIME/CORS/Range 与健康状态。传输未知结果和显式停用仍保留原有续传、撤权与精确清理,不重新下载已确认的 OSS 正文。页面或 Managed 引擎更新不能替代对应原生产物上线。 通用公开选择通过 V8.Method.GetPublicResourceBackupUrls({ ObjectKeys }) 和 Managed mci-file-resource-public-url 返回 GenericPublicResourceRoutes/1。前端先读取受限 Binding,再批量解析最多 100 个对象键;响应只含当前租户、公有源映射、备用 origin、公有桶及配置摘要,不返回私桶名或凭据。当前候选改动以下七个客户端文件,均须和后端一起部署后逐入口验收: 文件 | 实际接线 | 范围限制 | utils/public-resource-router.mjs | 校验原生 Binding、对象键、版本与路由回执,缓存绑定变化即失效 | 是受控解析器,不拦截所有浏览器请求 | utils/diy.common.js | GetPublicResourceRoute 引导配置、去重批量解析;导航前可做有界 HEAD | 同步 GetServerPath 不改为全局替换层 | diy-imgupload.vue | 明确公开图片首次 src/预览赋值前等待解析;图片错误最多一次原址回退 | 私有图片继续已有鉴权;不持久化 NAS 临时运行地址 | diy-fileupload.vue | 明确公开附件首次链接及下载、Office/CAD 直接导航前解析 | ZIP 聚合及下游 Office 服务独立取源未覆盖 | diy-components/DiyTableSpecialCell.vue | 公共表格图片、预览列表、附件 href 和点击时重解析 | 私有/角色受限走原授权;被拒绝对象不生成链接 | mixins/diy-common.mixin.js | 公共卡片封面/头像先占位,再解析和 HEAD;图片错误回退 | 系统头像和角色受限字段保持原私有链路;getFirstImageUrl/getImagePreviewList 仅补 helper,当前未发现活动调用点,不能据此增加页面覆盖数 | diy-richtext.vue | 新上传的公开图片/视频/附件插入编辑器前解析和 HEAD;保存时还原稳定主地址 | 不扫描或改写已有 HTML,重开历史正文仍可能请求主地址 | 上述消费者只选择明确 Limit:false 的公开分支,缺失标记不升级为公开;后端 ACL 才是权威。异步完成时重新核对租户、表单、行、字段、文件和组件身份,禁止陈旧响应覆盖当前值。可观测图片失败最多一次回退原地址;下载跳转、编辑器媒体和卡片背景在 HEAD 成功之后的中途传输失败不能统一捕获,不承诺全程无损回退。 **尚未覆盖:**私有对象通用分流、没有可信已提交依赖清单的独立 JS/CSS、已有富文本 HTML、AI/Three.js 硬编码绝对 URL 或直接 loader、平台 index.html / bootstrap 在系统配置加载前请求的 JS/CSS、其它未接入的播放器和客户端。禁止 DOM 全局字符串替换来掩盖这些缺口;历史热门文件已复制不等于全部消费者已转向 NAS。 交付须分开记录源码/离线测试、后端与应用包部署、真实浏览器选址、NAS 完整字节与权限、OSS/CDN 可比小时请求及完整北京时间自然日账单。复制本身的流量单列;完整日服务可用且新代码覆盖确定前,不计算或承诺每日节省金额。 💡 安装 MinIO 方法见:Docker 部署文档 (https://microi.blog.csdn.net/article/details/143576299) [MinIO配置: https://file.microi.net/upload/img/csdn/0bde20907de743f5b051036546837afa.png#pic_center] --- ## 🌍 亚马逊 S3 📌 首先请熟悉亚马逊 S3:亚马逊 S3 入门 (https://blog.csdn.net/qq973702/article/details/143648974) 平台使用 MinIO SDK 驱动亚马逊 S3,后续将补充详细配置说明。