@microi.net/cli 5.8.5 → 5.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/.codebuddy-plugin/marketplace.json +2 -2
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.workbuddy-plugin/marketplace.json +2 -2
  5. package/.workbuddy-plugin/plugin.json +1 -1
  6. package/LICENSE +21 -21
  7. package/README.md +71 -71
  8. package/assets/build-meta.json +7 -7
  9. package/assets/feature-matrix.json +138 -138
  10. package/assets/logo.svg +4 -4
  11. package/cordis.patch.yml +1 -1
  12. package/package.json +1 -1
  13. package/scripts/mcp-codex-stdio-adapter.js +0 -0
  14. package/scripts/mcp-server.js +1 -1
  15. package/scripts/microi-cli.js +66 -65
  16. package/scripts/microi-codex-broker.js +450 -450
  17. package/scripts/microi-codex-router.js +618 -618
  18. package/scripts/microi-skills.meta.json +384 -384
  19. package/skills/.microi-skills-version.json +2 -2
  20. package/skills/.progressive-disclosure-manifest.json +21 -21
  21. package/skills/README.md +287 -287
  22. package/skills/ai-engine/SKILL.md +269 -265
  23. package/skills/ai-engine/agents/openai.yaml +4 -4
  24. package/skills/ai-engine/references/ai-employees.md +48 -48
  25. package/skills/ai-engine/references/self-hosted-digital-human.md +59 -59
  26. package/skills/ai-platform-governance/SKILL.md +177 -177
  27. package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -190
  28. package/skills/app-store/SKILL.md +525 -525
  29. package/skills/app-store/agents/openai.yaml +4 -4
  30. package/skills/business-blueprint/SKILL.md +193 -193
  31. package/skills/datasource-engine/SKILL.md +93 -93
  32. package/skills/datasource-engine/agents/openai.yaml +4 -4
  33. package/skills/dos-orm/SKILL.md +97 -95
  34. package/skills/dos-orm/references/api-reference.md +229 -229
  35. package/skills/email-engine/SKILL.md +81 -81
  36. package/skills/email-engine/references/v8-email.md +34 -34
  37. package/skills/job-engine/SKILL.md +176 -176
  38. package/skills/job-engine/agents/openai.yaml +4 -4
  39. package/skills/message-notification/SKILL.md +156 -156
  40. package/skills/message-notification/agents/openai.yaml +5 -5
  41. package/skills/message-notification/references/contracts.md +102 -102
  42. package/skills/microi/SKILL.md +14 -14
  43. package/skills/microi-ai-app-auth.js +652 -652
  44. package/skills/microi-ai-application/SKILL.md +115 -115
  45. package/skills/microi-ai-application/agents/openai.yaml +4 -4
  46. package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
  47. package/skills/microi-client-frontend/SKILL.md +246 -244
  48. package/skills/microi-client-frontend/references/progressive-01-3-/345/212/250/346/200/201/346/214/211/351/222/256/347/263/273/347/273/237.md +144 -144
  49. package/skills/microi-client-frontend/references/progressive-02-8-/350/277/220/350/241/214/346/227/266/351/253/230/351/242/221/345/235/221/345/244/215/347/233/230.md +196 -196
  50. package/skills/microi-client-frontend/references/progressive-03-vue3-/345/211/215/347/253/257/345/276/256/346/234/215/345/212/241/345/256/277/344/270/273/350/247/204/345/210/231.md +167 -167
  51. package/skills/microi-codex/SKILL.md +102 -100
  52. package/skills/microi-codex-installer/SKILL.md +231 -231
  53. package/skills/microi-codex-installer/agents/openai.yaml +7 -7
  54. package/skills/microi-datasource-mapping/SKILL.md +122 -122
  55. package/skills/microi-db-schema/SKILL.md +175 -175
  56. package/skills/microi-db-schema/agents/openai.yaml +4 -4
  57. package/skills/microi-db-schema/references/core-tables.md +695 -695
  58. package/skills/microi-db-schema/references/form-component-options.md +256 -256
  59. package/skills/microi-db-schema/references/schema-overview.md +202 -202
  60. package/skills/microi-db-schema/references/schema.md +646 -646
  61. package/skills/microi-db-schema/references/table-catalog.md +1599 -1599
  62. package/skills/microi-deployment/SKILL.md +222 -220
  63. package/skills/microi-deployment/references/deployment-matrix.md +109 -109
  64. package/skills/microi-docs-coverage/SKILL.md +133 -133
  65. package/skills/microi-docs-coverage/references/capability-map.md +91 -91
  66. package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
  67. package/skills/microi-form-engine/SKILL.md +333 -333
  68. package/skills/microi-form-engine/references/component-catalog.md +218 -218
  69. package/skills/microi-form-engine/references/data-source-events.md +124 -124
  70. package/skills/microi-form-layout/SKILL.md +205 -205
  71. package/skills/microi-form-layout/references/progressive-01-3-/344/270/211/347/247/215/345/210/206/347/273/204/347/232/204/345/255/230/345/202/250/344/270/216/351/205/215/347/275/256.md +235 -235
  72. package/skills/microi-frontend-sdk/SKILL.md +194 -194
  73. package/skills/microi-frontend-sdk/references/progressive-01-token-/345/275/223/345/211/215/347/231/273/345/275/225/347/224/250/346/210/267/344/270/216/345/275/223/345/211/215/347/273/210/347/253/257/347/231/273/345/275/225/345/215/217/350/256/256.md +176 -176
  74. package/skills/microi-left-right-layout/SKILL.md +141 -141
  75. package/skills/microi-microservice/SKILL.md +328 -326
  76. package/skills/microi-microservice/references/runtime-delivery.md +278 -278
  77. package/skills/microi-mobile-app-quality/SKILL.md +185 -185
  78. package/skills/microi-mobile-app-quality/references/progressive-01-4-/351/207/215/350/246/201/346/214/211/351/222/256/345/277/205/351/241/273/345/270/246/345/233/276/346/240/207.md +213 -213
  79. package/skills/microi-mobile-app-quality/references/progressive-02-9-/344/270/273/351/242/230/345/210/207/346/215/242/345/277/205/351/241/273/347/234/237/345/256/236/344/270/224/345/205/250/345/261/200/347/224/237/346/225/210.md +128 -128
  80. package/skills/microi-solution-quotation/SKILL.md +78 -78
  81. package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
  82. package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
  83. package/skills/microi-sso/SKILL.md +96 -90
  84. package/skills/microi-sso/references/acceptance.md +49 -49
  85. package/skills/microi-sso/references/configuration-and-security.md +53 -53
  86. package/skills/microi-sso/references/inbound.md +53 -53
  87. package/skills/microi-sso/references/outbound.md +39 -39
  88. package/skills/microi-system-delivery/SKILL.md +137 -137
  89. package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +193 -193
  90. package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +217 -217
  91. package/skills/microi-ui/SKILL.md +192 -192
  92. package/skills/microi-ui/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/234/272/346/231/257/350/223/235/345/233/276.md +183 -183
  93. package/skills/microi-uniapp-frontend/SKILL.md +193 -193
  94. package/skills/microi-uniapp-frontend/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/210/206/347/261/273-/345/217/214/346/240/217/345/210/227/350/241/250/347/213/254/347/253/213/346/273/232/345/212/250.md +225 -225
  95. package/skills/microi-uniapp-frontend/references/progressive-02-/345/205/263/351/224/256/344/270/232/345/212/241/350/265/204/344/272/247/344/270/215/345/276/227/351/273/230/350/256/244/351/200/211/344/270/255.md +154 -154
  96. package/skills/microi.v8.js +1921 -1921
  97. package/skills/module-engine/SKILL.md +255 -255
  98. package/skills/module-engine/references/module-config.md +204 -204
  99. package/skills/ocr-engine/SKILL.md +113 -113
  100. package/skills/ocr-engine/agents/openai.yaml +4 -4
  101. package/skills/page-engine/SKILL.md +206 -206
  102. package/skills/page-engine/examples/compact-dashboard.json +1444 -1444
  103. package/skills/page-engine/references/progressive-01-/346/211/200/346/234/211/347/273/204/344/273/266/347/261/273/345/236/213.md +234 -234
  104. package/skills/page-engine/references/progressive-02-/347/211/210/346/234/254/345/216/206/345/217/262-/345/271/266/345/217/221/344/277/235/345/255/230/344/270/216/345/233/236/346/273/232.md +60 -60
  105. package/skills/performance-testing/SKILL.md +221 -221
  106. package/skills/playwright-e2e/SKILL.md +196 -196
  107. package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +173 -173
  108. package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +183 -183
  109. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +221 -221
  110. package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +115 -115
  111. package/skills/print-engine/SKILL.md +259 -259
  112. package/skills/production-readonly-audit/SKILL.md +41 -41
  113. package/skills/report-engine/SKILL.md +71 -71
  114. package/skills/report-engine/agents/openai.yaml +4 -4
  115. package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
  116. package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
  117. package/skills/scripts/sync-embedded-skills.mjs +46 -46
  118. package/skills/scripts/validate-progressive-disclosure.mjs +57 -57
  119. package/skills/search-engine/SKILL.md +75 -75
  120. package/skills/search-engine/agents/openai.yaml +4 -4
  121. package/skills/spider-engine/SKILL.md +190 -190
  122. package/skills/system-observability/SKILL.md +249 -246
  123. package/skills/system-observability/references/memory-incident-triage.md +77 -77
  124. package/skills/translate-engine/SKILL.md +140 -140
  125. package/skills/translate-engine/agents/openai.yaml +4 -4
  126. package/skills/ui-design/SKILL.md +223 -223
  127. package/skills/ui-design/assets/pattern-showcase/app.js +54 -54
  128. package/skills/ui-design/assets/pattern-showcase/index.html +163 -163
  129. package/skills/ui-design/assets/pattern-showcase/styles.css +311 -311
  130. package/skills/ui-design/assets/templates/MCI-DESIGN.md +206 -206
  131. package/skills/ui-design/references/design-pattern-library.md +184 -184
  132. package/skills/ui-design/references/mci-design-contract.md +163 -163
  133. package/skills/ui-design/references/motion-and-media.md +78 -78
  134. package/skills/ui-design/references/product-flow-recipes.md +94 -94
  135. package/skills/ui-design/references/progressive-01-/351/242/234/350/211/262/344/275/223/347/263/273-css-variables-/346/224/257/346/214/201/344/270/273/351/242/230/345/210/207/346/215/242.md +218 -218
  136. package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +164 -164
  137. package/skills/ui-design/references/progressive-03-/345/212/250/346/225/210/350/247/204/350/214/203-/344/270/260/345/257/214/344/275/206/344/270/215/345/215/241.md +235 -235
  138. package/skills/ui-design/references/progressive-04-/347/273/204/344/273/266/351/243/216/346/240/274/351/200/237/346/237/245.md +152 -152
  139. package/skills/ui-design/references/progressive-05-/347/247/273/345/212/250/347/253/257/344/270/223/347/224/250/350/247/204/350/214/203.md +238 -238
  140. package/skills/ui-design/references/progressive-06-/344/270/273/351/242/230/345/210/207/346/215/242/345/256/236/347/216/260.md +194 -194
  141. package/skills/ui-design/references/progressive-07-/351/200/237/346/237/245-/344/273/216/345/244/264/346/220/255/345/273/272/344/270/200/344/270/252/347/247/273/345/212/250/347/253/257/351/241/265/351/235/242.md +207 -207
  142. package/skills/ui-design/references/progressive-08-/350/241/250/345/215/225/345/210/206/347/273/204/350/247/204/350/214/203-tabs-vs-collapsegroup-/345/274/272/345/210/266.md +142 -142
  143. package/skills/uniapp-mall-assets/SKILL.md +176 -176
  144. package/skills/unity-integration/SKILL.md +171 -171
  145. package/skills/unity-integration/agents/openai.yaml +4 -4
  146. package/skills/unity-integration/references/ai-app-delivery.md +119 -119
  147. package/skills/unity-integration/references/sdk-api.md +82 -82
  148. package/skills/unity-integration/references/toolbox-migration.md +66 -66
  149. package/skills/unity-integration/references/webgl-hosting.md +57 -57
  150. package/skills/v8-api-config/SKILL.md +388 -388
  151. package/skills/v8-cache-pattern/SKILL.md +312 -312
  152. package/skills/v8-crud-api/SKILL.md +178 -178
  153. package/skills/v8-crud-api/references/progressive-01-/346/237/245/350/257/242/345/210/227/350/241/250-/345/210/206/351/241/265.md +226 -226
  154. package/skills/v8-crud-api/references/progressive-02-where-/346/235/241/344/273/266/350/257/255/346/263/225/351/200/237/346/237/245.md +49 -49
  155. package/skills/v8-debugging/SKILL.md +284 -284
  156. package/skills/v8-explorer-tree/SKILL.md +228 -228
  157. package/skills/v8-export-import/SKILL.md +219 -219
  158. package/skills/v8-export-import/references/progressive-01-excellayout-/351/253/230/347/272/247/350/207/252/347/224/261/345/270/203/345/261/200.md +211 -211
  159. package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +202 -202
  160. package/skills/v8-export-import/references/progressive-03-/345/256/211/345/205/250-/346/200/247/350/203/275/346/263/250/346/204/217.md +42 -42
  161. package/skills/v8-file-upload/SKILL.md +284 -284
  162. package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +263 -263
  163. package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +161 -161
  164. package/skills/v8-formengine-http/SKILL.md +238 -238
  165. package/skills/v8-frontend-events/SKILL.md +180 -180
  166. package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
  167. package/skills/v8-frontend-events/references/bluetooth-print.md +258 -258
  168. package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +219 -219
  169. package/skills/v8-http-integration/SKILL.md +182 -182
  170. package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +220 -220
  171. package/skills/v8-http-integration/references/progressive-02-/351/224/231/350/257/257/345/244/204/347/220/206/346/250/241/345/274/217.md +44 -44
  172. package/skills/v8-image-processing/SKILL.md +190 -190
  173. package/skills/v8-image-processing/agents/openai.yaml +4 -4
  174. package/skills/v8-image-processing/references/api-reference.md +623 -623
  175. package/skills/v8-menu-buttons/SKILL.md +186 -186
  176. package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +222 -222
  177. package/skills/v8-menu-buttons/references/progressive-02-8-/346/250/241/345/274/217-f-/345/220/216/345/217/260/344/273/273/345/212/241/346/214/211/351/222/256-/351/225/277/344/273/273/345/212/241.md +228 -228
  178. package/skills/v8-menu-buttons/references/progressive-03-10-/345/217/215/346/250/241/345/274/217-/351/201/277/345/205/215.md +104 -104
  179. package/skills/v8-mongodb/SKILL.md +200 -200
  180. package/skills/v8-mq-mqtt/SKILL.md +176 -176
  181. package/skills/v8-mq-mqtt/references/mqtt-production.md +342 -342
  182. package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +181 -181
  183. package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
  184. package/skills/v8-saas-multi-tenant/SKILL.md +305 -305
  185. package/skills/v8-security/SKILL.md +210 -210
  186. package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +200 -200
  187. package/skills/v8-security/references/progressive-02-7-/346/227/245/345/277/227/350/256/260/345/275/225.md +160 -160
  188. package/skills/v8-sql-query/SKILL.md +302 -302
  189. package/skills/v8-table-event/SKILL.md +176 -176
  190. package/skills/v8-table-event/references/progressive-01-informv8-js-/350/241/250/345/215/225/346/211/223/345/274/200/344/272/213/344/273/266.md +216 -216
  191. package/skills/v8-table-event/references/progressive-02-/345/211/215/347/253/257/344/272/213/344/273/266/345/220/215-v8-eventname-/345/217/257/350/203/275/347/232/204/345/200/274.md +46 -46
  192. package/skills/v8-tcp-integration/SKILL.md +147 -147
  193. package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
  194. package/skills/v8-template-engine/SKILL.md +167 -167
  195. package/skills/v8-utilities/SKILL.md +104 -104
  196. package/skills/v8-utilities/references/client-api-index.md +143 -143
  197. package/skills/v8-utilities/references/platform-http-routes.md +83 -83
  198. package/skills/v8-utilities/references/server-api-index.md +188 -188
  199. package/skills/v8-workflow/SKILL.md +252 -252
  200. package/skills/v8-workflow/references/progressive-01-/350/212/202/347/202/271/345/274/200/345/247/213-v8-/344/272/213/344/273/266.md +180 -180
  201. package/skills/v8-workflow/references/workflow-configuration.md +49 -49
  202. package/skills/vision-engine/SKILL.md +160 -160
  203. package/skills/vision-engine/agents/openai.yaml +4 -4
  204. package/skills/vision-engine/references/architecture-and-acceptance.md +194 -194
  205. package/skills/workspace-conventions/SKILL.md +273 -273
  206. package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +209 -209
  207. package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +217 -217
  208. package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +27 -27
@@ -1,284 +1,284 @@
1
- ---
2
- name: v8-file-upload
3
- description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI 应用发布、V8.FilesByteBase64、V8.Method.Upload、私有文件 URL、文件响应、HDFS、OSS、MinIO 和 S3 存储。
4
- ---
5
-
6
- > **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
7
-
8
- # Microi V8 文件上传下载
9
-
10
- 你正在为 Microi 吾码平台编写文件上传/下载/返回相关代码。平台分布式存储(HDFS)支持阿里云OSS、MinIO、亚马逊S3,存储方案由 SaaS 引擎按租户配置。上传对象的 `Content-Type` 由框架按实际对象路径统一确定,覆盖 HTML、JS/MJS、CSS、JSON/MAP、SVG、字体与 WASM,未知后缀保留 `application/octet-stream`。MinIO、S3、OSS 普通与分片上传使用同一映射;该映射不替代上传权限、内容和配额校验。旧对象不会因后端升级自动改变元数据:先核对字节数和 SHA-256,再修正类型或按正式应用发布流程产生新版本。不要把源码与公有编译产物混在一起,也不要关闭 `nosniff` 掩盖类型错误。
11
-
12
- 公开入口覆盖 `V8.uploadFile`、多文件 `V8.uploadFiles` 与 MCP `microi_upload_file_base64`。多文件上传必须限制并发、逐文件返回结果;Base64 工具只接受明确文件名、大小和租户内目标范围,写后回读路径、大小与哈希。
13
-
14
- ## MCP stdio 与完整包的传输边界
15
-
16
- - 标准 MCP 服务的 stdio 使用固定 `128 MiB`(`134217728` 字节)未解析 JSON-RPC 缓冲,超过边界立即拒绝并关闭连接;不读取动态环境变量放大。SDK 调用端若仍使用
17
- 默认 `10 MiB` 缓冲,大响应仍可能关闭客户端,必须核对调用端支持的真实边界。
18
- - 该限制计算 UTF-8 wire 字节,包含完整 JSON 信封、Base64 和重复 `Data/Result` 正文。单个 Base64 字段的理论原始字节上限约 `96 MiB` 减信封,不能据此保证
19
- 任意 `64 MiB` 包响应都通过,也不能把它当成 HDFS `256 MiB` 应用包能力的新上限。
20
- - 已存在的正规完整包协议仍可使用 `PreparedPersistPackageByteBase64`;先按精确正文计算编码和整个请求/响应字节数,保留版本、CAS、哈希、HDFS 回读及快照 finalize。
21
- 连接关闭属于结果未知,先回读同版本、同请求身份,不能换请求键盲重传。
22
- - 超过 wire 能力的源码、运行资产优先使用已有目录/文件 stream 或分片协议及其
23
- 小型指针/回执,不把私有源码、超大资产常态化塞入 JSON/Jint;支持更大正文的其它
24
- 连接也须有明确边界。不能用增大stdio缓冲绕过能力鉴权、业务配额或包完整性检查。
25
-
26
- ## 表单字段的公有桶与私有桶(强制)
27
-
28
- - `Config.FileUpload.EnableRolePermission=true` 的字段强制私有上传,优先级高于字段和请求 `Limit`。
29
- 每个附件 `VisibleRoleIds` 多选真实角色,三个默认关闭的开关为 `HideUnauthorizedFiles`、
30
- `ShowUnauthorizedFileName`、`DisableRoleInheritance`。无权限时后端仅返回脱敏占位,不含 Path、
31
- Versions 或签名地址;普通保存必须保留无权附件,不能改角色、改名、删除或通过复制路径重新授权。
32
- 上传响应的 `_UploadProof` 绑定当前租户、用户、字段、记录、私有路径及有效期,只用于首次保存校验,
33
- 持久化时移除;新文件须携带当前记录/字段上下文上传。历史公有附件需重新私有上传后才能设置角色。
34
- 完整配置、继承与 MCP 流程见 `../microi-form-engine/SKILL.md`。
35
-
36
- ### 旧上传返回与接口引擎扩展
37
-
38
- - 系统设置 `CompatiblePlatformOldVersion` 默认关闭,空值/缺字段也关闭;开启时旧 HTTP 上传
39
- 保留 `Code/Data`,单文件也统一返回数组(包括 `Multiple=false`),补 `url/type/size/duration/uploading/progress/path/name/id`。
40
- 图片 `type=image`,名称去掉最后一个扩展名;`url` 必须沿用实际 `Url`,不能把私有文件改拼公有地址。
41
- - `/api/HDFS/upload`、`/api/Upload`、`/apiengine/platform-hdfs-upload` 使用当前租户上传引擎。
42
- 只有主库确认完整地址、别名及固定 Key 缺失才执行编译兜底;禁用、StopHttp、拒绝或异常不兜底。
43
- - `V8.Method.UploadCurrentRequestAsync()` 复用已验证的当前请求文件流,重复调用只上传一次;
44
- `V8.Method.IsLegacyUploadCompatibilityEnabled()` 读取宿主取得的开关,二者均不接受参数。
45
- 它们绑定当前租户与选定引擎,普通脚本或嵌套其它引擎不能借用;无请求时使用原有 `V8.Method.Upload`。
46
- - 返回编排由系统设置应用拥有的 Managed `platform-hdfs-upload` 实现;持久定制使用
47
- CreateIfMissing `platform-hdfs-upload-hook`,返回 `{Code:1, UploadResult:完整结果}`。
48
- 先用 `JSON.parse(JSON.stringify(V8.Param.Result))` 转为普通 JS 对象,再判断 `Data` 数组并遍历追加字段;不能直接对 CLR 包装对象使用 `Array.isArray` 或 `length` 分支。
49
- 系统设置及基础空库包都交付可空字段,但上传引擎只由系统设置包拥有;不携带租户开关值。
50
- - MCP 复用 `microi_add_field`、`microi_update_field`、`microi_create_engine`、
51
- `microi_save_engine_code` 和管理员回读;验收分别覆盖真实上传、配置缓存、引擎优先、缺失及错误分支。
52
-
53
- - `ImgUpload`、`FileUpload`、`RichText` 的“禁止匿名访问”是字段权威策略:`Limit=false` 写公有桶,`Limit=true` 写私有桶。普通用户只要通过当前菜单/表的新增或编辑动作授权,也必须按该字段配置执行;不得按用户等级把全部非超级管理员上传统一改成私有桶。
54
- - 浏览器上传必须携带 `FormEngineKey + FieldId + SysMenuId`,编辑已有记录再带 `FormDataId`,TableChild 再带父子授权上下文。后端先用 FormEngine 校验动作权限,再从当前租户回读 `diy_field.Component/Config`,用权威 `Limit` 覆盖请求值,并把目录固定为 `ImgUpload→img`、`FileUpload→file`、`RichText→editor`。
55
- - 客户端 `Limit`、`Path`、字段 Id 和菜单 Id 都只是待验证线索。没有可验证字段上下文的普通交互式上传默认私有并限制到安全一级目录;不能为了恢复公有字段语义而重新信任裸 `Limit=false`。
56
- - 兼容旧移动端/定制页面时,在普通系统设置 `sys_config.HdfsUploadRules` 配置目录与角色规则,无需逐个改客户端。`Path` 使用区分大小写的租户内相对路径;`RoleIds` 是真实角色 Id 数组,或显式启用 `AllAuthenticated`;`IncludeSubdirectories` 和 `AllowPublic` 默认关闭。只有后端有效角色命中且显式允许公有,才尊重请求 `Limit=false`。规则不是秘密,不必放入后端私有设置,修改权限与服务端判定必须严格受控。
57
- - 排查目录授权失败时先检查 multipart 是否同时提交 `Path` 和 `path`。客户端只应提交一个 `Path`;后端对相同值归一,对不同值拒绝,不能因重复字段误判而扩大 `HdfsUploadRules`。
58
- - 后端 v8.2.9+ 的配置 `Path` 支持 `*`(单层任意字符)、`**`(零到多层目录,须独占层级)、`?`、`[abc]`、`[a-z]`、`[!0-9]`/`[^0-9]`、`{a,b}` 候选及组合;优先 `files/{inspection,quality}/**` 这类最小业务前缀,不能为省事直接向全部用户配置 `** + AllowPublic`。只匹配请求目录,不匹配文件名或服务端追加的年月。实际上传路径不接受通配符;保留目录先于 glob 校验,不能用宽泛规则绕过。最多512字符/规则、4层花括号、32候选、4096令牌;有界动态规划和有界语法缓存不得缓存租户授权结果。先更新全部后端节点再配置新语法,精确旧规则仍兼容。
59
- - 目录规则只扩展无字段上下文上传,不绕过表/菜单/行权限、租户隔离、保留路径、文件类型、内容检测及配额;不能授予访问密钥会话、私有文件读取或应用发布权限。规则通过系统设置共享缓存读取,保存/删除后失效;应用包只发布字段和可空 DDL,禁止附带会覆盖租户规则的配置数据。验收覆盖授权/未授权角色、路径边界、公私桶、禁用用户、跨租户、规则撤销及缓存生效。
60
- - 老 `ImgUpload/FileUpload` 缺失 `Limit` 时兼容为公有;老 `RichText` 缺失上传配置时默认私有。微信待审图片与裁剪/压缩 `_origin` 原图始终私有,字段公有配置不能放宽这些特殊边界。客户端保存与预览必须以上传响应的实际 `Limit` 为准。
61
-
62
- ## 交互式图片默认压缩(强制)
63
-
64
- - `ImgUpload`、PC、UniApp/H5/小程序以及 MCP 普通图片上传在未显式传 `Preview` 时,必须按 `Preview=true` 处理;字段设计器新配置默认开启。只有业务明确要求公开原始画质时才允许显式关闭,不能把“客户端漏传”解释为关闭。
65
- - 默认展示图目标为不超过约 `500 KB`、最长边不超过 `1920 px`;可按真实用途进一步收紧。压缩必须使用 Windows/Linux/容器一致的跨平台实现,并限制输入字节、像素总量与解码并发,避免超大图触发 OOM。
66
- - 压缩流程固定为“先将原始字节写入当前租户 HDFS 私有桶的 `_origin` 对象 → 生成压缩展示图 → 写入目标公有/私有位置 → 回读大小与格式”。即使展示图是公有资源,原图也只能保存在私有桶。
67
- - 压缩、解码或编码失败时必须失败关闭,返回可诊断错误;禁止静默把几十 MB 的未压缩原图发布到公有桶。返回字段中的 `Size` 应表示展示图实际字节数,`OriginalSize` 可用于管理员审计,但业务字段不得保存私有桶真实地址或签名 URL。
68
- - 历史治理只迁移超过目标体积的图片:先为旧对象补齐私有原图副本,再上传压缩展示图、原子更新全部业务引用并逐条回读。确认没有引用后才删除旧公有对象;批量任务要有清单、检查点、幂等映射和失败重试,不能边扫描边不可逆覆盖。
69
-
70
- ## 表单引擎图片裁剪(强制)
71
-
72
- - `ImgUpload.Crop` 支持 `Enabled`、`Mode=free/fixed/select`、`Ratio`、`CustomWidth/CustomHeight`以及 `AllowZoom/AllowRotate/AllowFlip`。`Enabled` 只表示表单用户的默认状态,不得当作裁剪能力总开关;新增/编辑表单把裁剪开关合并到紧凑上传面板,旁边同时显示公有/私有桶、单/多图与最大数量、压缩状态和最大体积。旧字段未配置时该开关默认关闭,但用户仍可主动开启。
73
- - 裁剪弹层必须同时提供“取消本次上传”“不裁剪直接上传”“应用裁剪并上传”三个不同动作。多图选择按队列逐张处理;直接上传只跳过当前图片的裁剪,不能取消或阻塞后续图片。
74
- - 多图模式下后端可能为每个单文件请求返回 `Data: [{...}]`,单图模式返回 `Data: {...}`。PC/移动端必须先归一化对象/数组,再按客户端 `uid` 替换上传占位项并生成预览 URL,禁止把接口成功误显示成空列表。
75
- - 开启裁剪时,前端必须上传最终裁剪图,并在同一 multipart 请求中以 `MicroiOriginalFile` 附带同名、未改动的原图及 `CropEnabled=true`。不得仅传坐标后由后端重演,否则 EXIF 方向、旋转或镜像可能导致前后端结果不一致。
76
- - 后端必须校验裁剪图与原图一一同名,两者字节数都计入单次限制和每日配额,但原图不计为第二个业务文件。未宣告裁剪却传原图、宣告裁剪却漏传/错配原图都要失败关闭。
77
- - HDFS 写入顺序固定为“未改动原图写入私有 `_origin` 对象 → 可选压缩裁剪图 → 写入业务展示图”。裁剪原图写入失败时不得发布展示图;即使 `Preview=false`,裁剪原图仍必须私有保留。不得返回或持久化原图真实路径。
78
-
79
- ## 富文本图片、视频与附件(强制)
80
-
81
- - `RichText.Limit=false` 只用于需匿名长期访问的官网公告、商品详情等公开正文;内部内容用 `true`。普通用户经表单新增/编辑授权后也按后端回读到的该字段配置选择桶;仅修改请求中的 `Limit` 不能改变策略,客户端必须以上传响应的实际 `Limit` 为准。
82
- - RichText 分别配置 `Image`、`Video`、`File` 的 `Enabled/MaxSize/MaxCount`;图片另传 `Preview/CompressMaxSize/CompressMaxWidth`,并继续遵循“原图私有、展示图公有或私有”的压缩链路。附件类型白名单用 `File.Accept` 进一步收紧,不能放宽服务端白名单。
83
- - 私有正文持久化 `/__microi_richtext_private__/...` 稳定对象标识,严禁保存对象存储签名 URL、`OpenPrivateFile` Ticket、DiyToken 或其它会过期的凭据。每次打开记录时携带 `FormEngineKey/FormDataId/FieldId/SysMenuId` 批量换取短效审计代理地址。
84
- - 私有文件后端授权必须重新校验当前租户、菜单、表、行和 RichText 字段,并确认所请求路径精确存在于 `img/video/source.src` 或 `a.href`;普通上传字段的对象/数组只认 `Path/FilePath/FilePathName`,不得递归把 `Name/Size/Metadata` 等任意标量当作路径。未经当前租户 FileServer 主机权威校验的绝对 HTTP(S) URL 不得等价为本地对象 Key;正文文字、`data-src/data-href`、脚本标签和前缀相似路径都必须失败关闭。
85
- - 外部匿名页面没有后台记录权限上下文,不能解析私有标识。公开文章应由设计者把 RichText 字段配置为 `Limit=false`,有该表单新增/编辑权限的用户即可按权威配置发布;不得通过延长私有 URL 有效期模拟公开资源。
86
- - 若业务明确要求撤稿后旧媒体地址立即停止返回字节,使用私有对象和专用批准引用代理:只接受固定业务标识,权威校验当前租户、不可变批准版本与精确媒体引用,读取前后复核发布/许可/撤回状态,限制 MIME/体积并返回 `no-store`;不开放裸路径,不返回签名 URL 或重定向。普通文件短链接口不能代替这种业务撤权能力。
87
- - 撤回授权与稳定请求键待办先在共享数据库事务提交,再执行 HDFS 删除。未知结果沿用原请求键探查重放,原上传请求摘要保持。删除源对象不等于删除 CDN 缓存;历史公开链接必须有官方失效任务和独立缓存字节读回证据,否则保留未确认状态,不把 `Offline`、隐藏前台或源站 `404` 记作完整撤回。
88
-
89
- 官网客户端读取私有文件统一调用 `/apiengine/platform-private-file-url`,提交 `FilePathName` 或有界 `FilePathNames`,并按资源类型提供权威定位参数:普通表单字段使用 `FormEngineKey + FormDataId + FieldId + SysMenuId`;用户头像使用 `ResourceKind=UserAvatar + ResourceId=用户Id`;菜单/部门导入模板分别使用 `MenuImportTemplate`、`DeptImportTemplate` 与对应记录 Id。CAD 私有派生预览使用 `ResourceKind=FormFieldDerivedPreview`,除表单四元组外必须同时提交字段中保存的 `OriginalFilePathName` 和单个派生 `FilePathName`;后端只接受同目录同 basename 的 DWG→`_preview.dxf`、STEP/STP→`_preview.stl` 唯一映射,并在对象存在后签名。文件柜对象使用 `ResourceKind=FileManagerObject`,`ResourceId` 必须与单个 `FilePathName` 大小写精确相同,并提交能力探针返回的当前租户权威 `SysMenuId`;此类签名只允许平台超级管理员 DiyToken 会话,访问密钥和普通菜单用户一律拒绝。后端会从权威字段或对象存储重新读取并精确匹配路径;管理员也不能只传裸路径绕过对象引用,普通客户端禁止换取私有文件原始 Byte/Stream。旧 `/api/HDFS/GetPrivateFileUrl` 与 `/api/HDFS/MallFileUrl` 只保留令牌格式兼容并转发同一 Managed 接口,新代码不得继续引用。
90
-
91
- <!-- microi-progressive:begin -->
92
- <!-- microi-progressive:chunk id=v8-file-upload-000 sha256=9402f7173710e8d110392f184119154a50187138ab7d698fce0b73b5f12123e2 -->
93
- ## 核心 API
94
-
95
- | API | 说明 |
96
- |-----|------|
97
- | `V8.FilesByteBase64` | 接收上传时携带的文件字典 `{ FileName: base64 }` |
98
- | `V8.Method.Upload({...})` | 服务端上传文件到 HDFS(推荐) |
99
- | `V8.Method.GetPrivateFileUrl({FilePathName})` | 生成私有桶临时访问 URL |
100
- | `V8.Method.CopyObject({FilePathName,Path,Limit})` | 当前租户同一桶内服务端复制,保留原对象;用于固定入口与版本快照 |
101
- | `V8.Method.ObjectExist({FilePathName,Limit})` | 检查当前租户公有/私有桶对象是否存在 |
102
- | `V8.Method.GetObjectSha256({FilePathName,Limit})` | 在服务端流式计算对象原始字节与旧版 Base64 文本的 SHA-256 和字节数;不把对象内容送入 V8 |
103
- | `V8.Method.ListObjects({Path,Limit,Recursive,Marker,MaxKeys})` | 分页列举当前租户前缀,单页最多 1000 个 |
104
- | `/apiengine/platform-private-file-url` | 官网 PC/UniApp 按菜单、记录、字段和对象引用换取私有文件短链 |
105
- | `V8.Http.GetResponse({Url}).RawBytes` | 下载远程文件为字节数组 |
106
- | 接口返回 `{ FileName, ContentType, FileByteBase64 }` | 接口直接响应文件 |
107
-
108
- 固定 CDN 应用回填优先使用服务端 `CopyObject`,公有桶复制编译资产、私有桶复制源码;`Limit` 在源与目标间保持一致,`Path` 和 `FilePathName` 均由后端收敛到当前租户。大对象用 `GetObjectSha256` 流式核对原对象和复制目标,公有体验路径仍须从 CDN 独立回读。历史版本目标已存在时须核对字节哈希,发现不同内容立即停止;固定根可在新版本验证后覆盖。`ListObjects` 必须分页并限制到单个应用前缀,不得把这些存储管理原子直接开放为匿名业务接口。大型私有文本的 `GetPrivateFileText` 字符串边界及原字节/最终ZIP验签规则见[已有详细参考](references/progressive-01-公有桶-vs-私有桶.md)。
109
-
110
- MinIO SDK 7 的复制签名不匹配还可能来自带参数 MIME:SDK 对源 `Content-Type` 签名,却在 HTTP 请求中额外保留 `StringContent` 的默认 MIME。先在独立夹具核对源 MIME、重复头和真实存储返回,再升级后端复制传输修复;不能去掉 `charset`、关闭签名校验或下载后重传来伪造通过。修复后必须同时验证公私桶原始字节、精确 MIME、源元数据及源对象保留,固定入口恢复另覆盖主库权威、真实共享租约、双节点竞争和新进程重跑。取消/超时后保持原请求键,先回读目标再决定是否继续。
111
-
112
- <!-- /microi-progressive:chunk -->
113
- <!-- microi-progressive:chunk id=v8-file-upload-001 sha256=d535a333639a9005f5d20f25e36e2753a11835380713c1bb063ae618e6cea4af -->
114
- ## 第三方数据库附件迁移
115
-
116
- 当第三方表只保存附件路径时,先用 `microi_inspect_external_database` / `microi_query_external_database` 或 `V8.Dbs.<DbKey>` 查询记录。`microi_import_external_attachment` 允许后端已确认的 `Level >= 9999` 当前用户直接提供 HTTP/HTTPS URL、API 节点可读的本机绝对路径或 UNC 路径。
117
-
118
- - 导入工具必须显式确认;HTTP、私网、重定向、本机和 UNC 均可访问,但最终能力受 API 服务进程账号、网络、磁盘及对象存储权限约束。
119
- - 下载与上传使用临时文件和文件流,不经过 Base64;不设固定 20/100 MB 上限,`MaxBytes=0` 或省略表示不设置 MCP 上限,可处理 200/500 MB 或更大文件。
120
- - 带签名参数或用户凭据的源 URL、鉴权 Header 和本机/UNC 路径不得出现在结果、日志或目标表;脱敏审计只记录来源 SHA-256、类型和字节数,目标字段只保存吾码租户内相对路径。
121
- - 使用第三方附件 Id/版本作为幂等键,回读目标记录后才标记成功;多节点重投不能重复产生业务附件。
122
- - 写入目标 `FileUpload` 字段前必须回读其权威 `diy_field.Config.FileUpload.Limit`;目标字段为 `Limit=true` 时,迁移上传也必须使用 `Limit=true` 写入私有桶,不得上传到公有桶后仅靠字段路径伪装为私有文件。
123
- - 源附件为空、大小为 `0` 或源对象不可读取时,不得创建目标业务记录并把上传字段保存为 `[]`、`'[]'` 或其它空占位值;应仅在迁移账本中记录为跳过或失败,保留来源 Id、原因和可重试状态。
124
- - 私有附件只有在目标记录回读成功,并使用该记录的权威资源上下文调用 `/apiengine/platform-private-file-url` 取得代理地址,再对该地址执行 `Range: bytes=0-0` 且确认返回 `200/206`、实际读到字节并且不是 JSON 错误后,才允许标记迁移成功;完整验收再核对总字节数或 SHA-256。
125
- - 批量迁移应落任务状态表并分页处理,失败可重试;不要让 MCP 一次加载整库路径或大文件集合。
126
-
127
- 可信后端 V8 可用 `V8.Http.GetResponse({ Url: url }).RawBytes` 下载,再用 `System.Convert.ToBase64String` 和 `V8.Method.Upload` 上传。该路径同样必须校验域名、大小、Content-Type、后缀和最终重定向目标。
128
-
129
- <!-- /microi-progressive:chunk -->
130
- <!-- microi-progressive:chunk id=v8-file-upload-002 sha256=176e9f91705f20416e177d7dbdbd32fb8f81dff6ba0da1a45b533795d187f0e0 -->
131
- ## 接收前端上传的文件
132
-
133
- 前端发起文件上传时,平台自动把文件以 base64 形式注入到 `V8.FilesByteBase64`:
134
-
135
- ```javascript
136
- // V8.FilesByteBase64 = { '文件名1.png': 'base64...', '文件名2.pdf': 'base64...' }
137
- if (!V8.FilesByteBase64) {
138
- return { Code: 0, Msg: '请上传文件' };
139
- }
140
-
141
- var fileNames = Object.keys(V8.FilesByteBase64);
142
- var firstFile = fileNames[0];
143
- var firstBase64 = V8.FilesByteBase64[firstFile];
144
-
145
- // 上传到 HDFS
146
- var upResult = V8.Method.Upload({
147
- FilesByteBase64: V8.FilesByteBase64,
148
- Limit: true, // 业务上传默认私有桶(需临时 URL 访问)
149
- Preview: false, // true=自动生成预览图
150
- Path: '/business/orders', // 存储路径前缀
151
- OsClient: V8.OsClient
152
- });
153
-
154
- if (upResult.Code !== 1) return upResult;
155
-
156
- // upResult.Data = [{ FileName, Path, FullPath, Size, ... }, ...]
157
- var filePath = upResult.Data[0].Path; // 相对路径,存数据库
158
- var fullUrl = upResult.Data[0].FullPath; // 完整 URL(公有桶)
159
- ```
160
-
161
- ### AI 应用超大资产断点续传
162
-
163
- Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进入 Base64、JSON 或 Jint。`microi_publish_application_directory_stream` 在协议 v3 下与 `@microi.net/cli` 共用同一套传输客户端:单文件大于 128 MiB 时自动切换为原始字节断点续传,小文件继续兼容旧版单请求链路。
164
-
165
- - 默认分片 16 MiB;5 GiB 文件为 320 片。分片通过 `application/octet-stream` 发送,必须携带精确 `Content-Length` 与 SHA-256。
166
- - 服务端逐片写入 HDFS 后重新流式回读校验;完成时按顺序合并、再次核对整文件 SHA-256,再生成不可变版本完整性标记。
167
- - 会话 Id 由租户、应用、版本、路径和文件摘要确定。网络中断或进程重启后先读取远端状态,只续传缺失分片;已成功的相同摘要请求直接幂等返回。
168
- - 吾码不为协议 v3 设置业务文件/目录字节上限,状态中的 `ApplicationAssetResumableProductSizeLimitBytes=0` 表示没有产品配置上限。每个对象仍受协议技术边界(最多 10000 片、单片最多 1 GiB)、JavaScript 安全整数、对象存储、磁盘、网关和网络条件约束。
169
- - 该链路只允许通过能力鉴权的当前租户超级管理员,并继续受 `DisableFileUpload` 负向总开关控制;它不是普通用户上传或任意 HDFS 路径写入接口。
170
- - 每个会话都在 `mci_ai_app_file` 保留审计记录,`StorageScope=ApplicationAssetMultipartSession`。管理员在 **系统引擎 → 超大文件上传记录** 查看状态、阶段、已传字节/分片、进度、心跳、错误和恢复建议;成功、失败和取消记录都不静默删除。
171
-
172
- ### 普通业务上传的分层限制
173
-
174
- 以下限制适用于 HTTP、表单、V8、移动端和旧版应用资产单请求,不适用于上面的受信任 v3 断点续传。Token 不是无限上传授权;普通入口必须在解码 Base64、解析图片或调用对象存储前执行服务端校验:
175
-
176
- 1. **租户业务配置**:有效正数/布尔值按 `sys_osclients` 当前租户 → 代码默认值解析。租户可以按业务需要提高或降低默认值,不要求安装者维护额外环境变量或修改 `appsettings`。
177
- 2. **平台绝对上限**:最终业务值再与代码内固定灾难保护上限取较小值,租户和安装参数都不能放大。
178
- 3. **HTTP 解析上限**:Kestrel 请求正文与 Multipart 固定为 2048 MB,普通表单单值固定为 128 MB,是所有租户共享的请求解析硬顶。
179
- 4. **字段级限制**:前端 `FileUpload` / `ImgUpload` / `RichText` 的 `MaxSize`、`MaxCount` 等只能与当前租户有效值取更小值,不能提高后端上限,也不能替代服务端校验。
180
-
181
- 业务配置与固定边界:
182
-
183
- | `sys_osclients` 字段 | 代码默认值 | 平台固定边界 |
184
- |---|---:|---:|
185
- | `DisableFileUpload` | `false`(关闭,即允许上传) | — |
186
- | `FileUploadMaxFileMB` | 100 MB | 1024 MB |
187
- | `FileUploadMaxRequestMB` | 200 MB | 2048 MB |
188
- | `FileUploadMaxCount` | 10 | 100 |
189
- | `FileUploadDailyUserQuotaMB` | 2048 MB | 10 TB |
190
- | `FileUploadDailyTenantQuotaMB` | 20480 MB | 10 TB |
191
-
192
- - `sys_osclients` 六个现行字段全部可空;`DisableFileUpload` 空值、无效值或 `0/false` 均表示允许上传,只有 `1/true` 才禁止。旧数据库尚未创建新物理字段时才回退读取 `FileUploadEnabled`,升级后旧正向字段即使为 `0` 也不得覆盖新字段的默认允许语义。`FileUploadMaxRequestMB` 指一次上传所有文件的业务合计大小,不等于 Kestrel HTTP 请求正文上限。
193
- - 固定灾难保护和 HTTP/Multipart/Form 解析上限不属于安装配置;租户值即使更大也会被这些边界截断。最终单次总量还不能超过帐号或租户的有效日额度,单文件不能超过最终单次总量。
194
- - `DisableFileUpload=1` 表示禁用当前租户上传,也会阻止 AI 应用资产断点续传;不能把内部发布协议当成绕过开关的后门。普通单请求继续受全局大小硬上限,v3 只移除产品级字节上限;租户配置刷新应走现有 SaaS 引擎重载和共享 Redis 发布订阅,不能依赖单节点内存。
195
- - 帐号与租户每日额度在共享 Redis 中用单次原子脚本预留,支持多节点;Redis 不可用时失败关闭,不能降级成无限上传。
196
- - 额度按 UTC 日期统计。为防并发重试绕过限制,后续对象存储失败也不退还已经预留的额度。
197
- - 每日额度只阻断短期滥用;对象存储必须另外配置租户/桶总容量、账单告警、生命周期与实际用量对账。Redis 计数不能作为长期容量事实源。
198
- - 标准表单字段上传按后端回读的 `diy_field.Config` 决定桶,不能按帐号等级覆盖字段策略。无字段上下文的普通上传默认只允许平台预定义私有一级目录;管理员可通过 `sys_config.HdfsUploadRules` 按真实角色明确扩展业务目录和公有权限,不能信任客户端自报角色或裸 `Limit:false`。源码、应用产物等仍使用独立受控发布流程。
199
- - 反向代理、Ingress/IIS 的请求体上限应与进程级 HTTP 解析硬顶协调。字段自身的类型、后缀、大小和数量配置只能进一步收紧当前租户有效值,不能替代平台硬顶。
200
-
201
- ### 复盘:“当前租户已停用文件上传”
202
-
203
- - 该提示只表示当前运行环境命中的 `sys_osclients.DisableFileUpload` 被明确设置为 `1/true`(或仍在旧库兼容期且 `FileUploadEnabled=0`)。新字段缺列以外的空值、无效值和新租户都按默认允许,不能先把问题归因于 MinIO/HDFS。
204
- - 新版响应同时返回 `DataAppend.ErrorType=TenantFileUploadDisabled`、`OsClient`、`ConfigField=DisableFileUpload`、`ExpectedValue=0` 和文档地址;客户端必须保留后端 `Msg/DataAppend`,不能只显示“上传失败”。
205
- - 处理时先读取当前 API 进程的 `OsClient + OsClientType + OsClientNetwork`,再精确回读同三元组的启用记录并把 `DisableFileUpload` 关闭为 `0`。不能仅按租户名批量覆盖其它网络或环境记录。
206
- - 保存后等待 SaaS 共享配置重载,再分别验证一个小公有图片和一个小私有文件。只有错误转为 endpoint、bucket、签名或 `Invalid URI` 后,才进入对象存储配置排查。
207
- - 不要通过删除 Redis 日额度 Key、扩大文件大小上限或改成公有桶来解除租户停用;这些动作与开关无关,还会扩大安全风险。
208
-
209
- ### AI / MCP 调整租户上传配额
210
-
211
- 用户明确授权修改某个租户的上传额度时,AI 可以直接使用标准 MCP 完成,不要把应用层提示误判成阿里云 OSS、MinIO 或 S3 的存储配额,也不要先清 Redis:
212
-
213
- 1. `microi_get_table_data(tableName: "sys_osclients")` 按 `OsClient`、`IsEnable=1` 查询,选择 `Id/OsClientType/OsClientNetwork`、`DisableFileUpload` 和五个现行 `FileUpload*` 配额字段;`FileUploadEnabled` 只用于诊断未升级旧节点。
214
- 2. 先以当前服务器的 `OsClientType + OsClientNetwork` 收窄到实际生效记录;只有用户明确要求多个环境保持一致时才扩展范围。逐条调用 `microi_update_form_data`,`row` 必须包含 `Id`,并传 `confirmExecution: "sys_osclients"`。
215
- 3. MB 是存储单位:`20 GB = 20480 MB`。可修改字段为 `DisableFileUpload`、`FileUploadMaxFileMB`、`FileUploadMaxRequestMB`、`FileUploadMaxCount`、`FileUploadDailyUserQuotaMB`、`FileUploadDailyTenantQuotaMB`;不要再写旧 `FileUploadEnabled`。
216
- 4. 保存后逐条远程回读;FormEngine 会排队重载 SaaS 运行配置,再用真实小文件上传做生效冒烟。只看到 MCP 返回“更新成功”不算验收。
217
- 5. 提高每日配额保留当天已用计数,剩余额度为新上限减已用量。计数按 UTC 日期,失败上传不退款;除非用户明确授权事故处置,不得删除 Redis 配额 Key。
218
-
219
- 租户 MCP 只能调整普通业务上传配置;平台固定灾难保护、HTTP/Multipart/Form 解析上限和反向代理上限不能通过 `sys_osclients` 绕过。协议 v3 的原始分片不读取普通单请求大小字段,但仍要求能力鉴权、总开关、版本快照、逐片/整文件哈希和审计。写入 `sys_osclients` 属于控制面操作,只允许当前租户的 `Level >= 9999` 管理身份,并且必须保留 MCP 审计与写后回读。
220
-
221
- ### UniApp / H5 客户端直传路径规则
222
-
223
- 移动端通过 `/api/HDFS/UniappUpload` 上传时,前端必须走 `microi.v8.js` 的 `V8.uploadFile`,不要在页面里手写 `uni.uploadFile`。客户端上传的 `Path` 与服务端 `V8.Method.Upload` 示例不同,必须是安全相对路径:
224
-
225
- - 正确:`mall/pay-proof`、`mall/member/avatar`、`order/proof`
226
- - 错误:`/mall/pay-proof`、`https://...`、`C:\...`、`../x`、`mall//x`、`~x`
227
- - multipart 请求不能带 `Content-Type: application/json`,否则后端可能读不到 `Path` 表单字段并返回“移动端文件上传路径不合法!”
228
- - `OsClient` 只能保留一个规范字段,避免同时提交 `OsClient`、`osclient` 或 query/header/formData 多处互相冲突。
229
- - 生产 H5 不能只依赖 `uni.uploadFile`。页面从 `uni.chooseImage` 得到的 `tempFiles[0].file`、`tempFiles[0]`、`blob:` / `data:` 临时路径都要传给 `V8.uploadFile`,并设置 `preferFetch:true`;SDK 必须能用 `fetch + FormData` 兜底,否则线上可能报 `未找到 MicroiV8 上传适配器。`。
230
-
231
- <!-- /microi-progressive:chunk -->
232
- <!-- microi-progressive:chunk id=v8-file-upload-003 sha256=d02dfde4abd2349cd92de1daee129bc08ba42142b5f6229736588cb3e0dbf43c -->
233
- ## 跨平台文件同步登录会话
234
-
235
- 文件柜、文件同步等需要连接另一套 Microi API 的工具,必须把远程平台视为独立登录会话:
236
-
237
- - 用户必须先完成远程登录,登录成功后显示远程用户名称、帐号、ApiBase、OsClient 和登录状态,并提供明确的退出登录操作。
238
- - 历史远程连接通过 `mci_` 前缀表保存,并按 `V8.CurrentUser.Id` 做行级隔离;不得把帐号、密码或 Token 放入 `localStorage`。
239
- - 密码和 Token 只能由受保护的接口引擎写入、读取和清理。数据库必须保存可校验的加密密文,普通 FormEngine 列表不得返回密文字段。
240
- - 密码和 Token 使用 `V8.Method.ProtectApiEngineSecret/UnprotectApiEngineSecret`,由宿主把密文绑定当前 `OsClient + ApiEngineKey`;不得从已脱敏的 `V8.OsClientModel` 读取 `AuthSecret/DbConn`,也不得使用进程级临时密钥。接口引擎 Key 必须稳定,确保服务重启和应用升级后仍能解密历史连接。
241
- - 历史连接列表只返回脱敏元数据;一键重连时再按记录 Id 和当前用户读取凭据。删除连接必须同时清除保存的密码和 Token。
242
- - 远程目标登录后必须调用文件柜能力探针 `mci_file_sync_capability` 检查同步协议版本,并使用其 `Data.FileManagerSysMenuId` 作为目标租户权威文件柜菜单;禁止硬编码发布端菜单 Id。接口不存在、未返回菜单 Id、返回 404/非标准结果或协议版本过低时,提示目标平台更新【文件柜】应用,不得继续同步。
243
- - 验收至少覆盖:登录成功显示身份、退出后 Token 清空、历史连接一键重连、删除连接、密文落库、服务重启后仍可解密、目标平台缺少能力接口时的升级提示。
244
-
245
- <!-- /microi-progressive:chunk -->
246
- <!-- microi-progressive:chunk id=v8-file-upload-004 sha256=8393c7b2b7ce36e563843f43f42e9a06a8cb69fc7b792930912353d846ee769f -->
247
- ## 空目录标记的受控恢复
248
-
249
- - 只需移除一个目录占位对象时,使用 `V8.HDFS.DeleteObject({FilePathName:'当前租户目录/', Limit:true/false, EmptyDirectoryOnly:true})` 并等待异步结果。必须是当前租户实际有效超级管理员的可信 V8/DiyToken 会话;不能使用参数中的 `_CurrentUser`、访问密钥或临时维护引擎代替授权。
250
- - HTTP/MCP 必须使用专用 `/api/HDFS/DeleteEmptyDirectoryMarker` / `microi_delete_empty_directory_marker`,显式选择桶并保留末尾 `/`。旧后端缺路由、非标准结果或成功缺少 `DeletionMode=EmptyDirectoryMarkerOnly` / `VerifiedAbsent=true` 均停止,绝不回退普通递归删除。
251
- - MCP 默认 dry run;执行 `confirmExecution` 精确等于 `filePathName`。旧 `/api/HDFS/DeleteObject` 不能作为缺少专用路由时的回退。路径拒绝租户根、跨租户、未知绝对前缀、URL、通配符与穿越。原始列表必须完整、大小明确为零,供应商只删精确标记 key,再完整回读;不允许前缀批量删除、忽略续页或把缺失 Size 默认成零。
252
- - 这不是零字节对象 CAS,也不证明历史 NoPUT。同 key 版本替换没有跨供应商统一条件 DELETE;执行前必须隔离原请求并排除活跃写入。保留历史/当前证据的区别;未知结果先只读回查,不换请求键、不清其它对象、不自动再次 PUT。
253
-
254
- ## 下载远程文件并存到 HDFS
255
-
256
- ```javascript
257
- // 1) 下载远程图片
258
- var resp = V8.Http.GetResponse({ Url: V8.Param.imageUrl });
259
- if (resp.StatusCode !== 200) return { Code: 0, Msg: '下载失败' };
260
-
261
- // 2) 转 base64 后上传到 HDFS
262
- var base64 = System.Convert.ToBase64String(resp.RawBytes);
263
- var fileName = V8.Method.NewGuid() + '.png';
264
-
265
- var upResult = V8.Method.Upload({
266
- FilesByteBase64: { [fileName]: base64 },
267
- Limit: false,
268
- Path: '/imported',
269
- OsClient: V8.OsClient
270
- });
271
-
272
- return upResult;
273
- ```
274
-
275
- > 在 V8/Jint 中避免把 `resp.RawBytes` 直接塞进 `FilesByte`;序列化时可能变成数字/浮点数组,导致 `Unexpected token when reading bytes`。更稳的是 `System.Convert.ToBase64String(resp.RawBytes)` 后使用 `FilesByteBase64`。
276
-
277
- 移动端公开图片优先使用 `.jpg` / `.png` / `.webp`。如果上传 `.svg`,必须确认对象存储返回正确 `Content-Type: image/svg+xml`,否则浏览器可能拦截或不渲染。
278
-
279
- <!-- /microi-progressive:chunk -->
280
- ## 详细参考路由(渐进披露)
281
- 仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。
282
- - [references/progressive-01-公有桶-vs-私有桶.md](references/progressive-01-公有桶-vs-私有桶.md):公有桶 vs 私有桶;接口直接响应文件(下载/导出);通过 URL 列表批量下载并入库
283
- - [references/progressive-02-office-文件在线编辑版本号规则.md](references/progressive-02-office-文件在线编辑版本号规则.md):Office 文件在线编辑版本号规则;ImgUpload / FileUpload 字段值兼容规则;安全注意
284
- <!-- microi-progressive:end -->
1
+ ---
2
+ name: v8-file-upload
3
+ description: Microi V8 与 MCP 文件上传下载指南。用于处理流式 AI 应用发布、V8.FilesByteBase64、V8.Method.Upload、私有文件 URL、文件响应、HDFS、OSS、MinIO 和 S3 存储。
4
+ ---
5
+
6
+ > **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
7
+
8
+ # Microi V8 文件上传下载
9
+
10
+ 你正在为 Microi 吾码平台编写文件上传/下载/返回相关代码。平台分布式存储(HDFS)支持阿里云OSS、MinIO、亚马逊S3,存储方案由 SaaS 引擎按租户配置。上传对象的 `Content-Type` 由框架按实际对象路径统一确定,覆盖 HTML、JS/MJS、CSS、JSON/MAP、SVG、字体与 WASM,未知后缀保留 `application/octet-stream`。MinIO、S3、OSS 普通与分片上传使用同一映射;该映射不替代上传权限、内容和配额校验。旧对象不会因后端升级自动改变元数据:先核对字节数和 SHA-256,再修正类型或按正式应用发布流程产生新版本。不要把源码与公有编译产物混在一起,也不要关闭 `nosniff` 掩盖类型错误。
11
+
12
+ 公开入口覆盖 `V8.uploadFile`、多文件 `V8.uploadFiles` 与 MCP `microi_upload_file_base64`。多文件上传必须限制并发、逐文件返回结果;Base64 工具只接受明确文件名、大小和租户内目标范围,写后回读路径、大小与哈希。
13
+
14
+ ## MCP stdio 与完整包的传输边界
15
+
16
+ - 标准 MCP 服务的 stdio 使用固定 `128 MiB`(`134217728` 字节)未解析 JSON-RPC 缓冲,超过边界立即拒绝并关闭连接;不读取动态环境变量放大。SDK 调用端若仍使用
17
+ 默认 `10 MiB` 缓冲,大响应仍可能关闭客户端,必须核对调用端支持的真实边界。
18
+ - 该限制计算 UTF-8 wire 字节,包含完整 JSON 信封、Base64 和重复 `Data/Result` 正文。单个 Base64 字段的理论原始字节上限约 `96 MiB` 减信封,不能据此保证
19
+ 任意 `64 MiB` 包响应都通过,也不能把它当成 HDFS `256 MiB` 应用包能力的新上限。
20
+ - 已存在的正规完整包协议仍可使用 `PreparedPersistPackageByteBase64`;先按精确正文计算编码和整个请求/响应字节数,保留版本、CAS、哈希、HDFS 回读及快照 finalize。
21
+ 连接关闭属于结果未知,先回读同版本、同请求身份,不能换请求键盲重传。
22
+ - 超过 wire 能力的源码、运行资产优先使用已有目录/文件 stream 或分片协议及其
23
+ 小型指针/回执,不把私有源码、超大资产常态化塞入 JSON/Jint;支持更大正文的其它
24
+ 连接也须有明确边界。不能用增大stdio缓冲绕过能力鉴权、业务配额或包完整性检查。
25
+
26
+ ## 表单字段的公有桶与私有桶(强制)
27
+
28
+ - `Config.FileUpload.EnableRolePermission=true` 的字段强制私有上传,优先级高于字段和请求 `Limit`。
29
+ 每个附件 `VisibleRoleIds` 多选真实角色,三个默认关闭的开关为 `HideUnauthorizedFiles`、
30
+ `ShowUnauthorizedFileName`、`DisableRoleInheritance`。无权限时后端仅返回脱敏占位,不含 Path、
31
+ Versions 或签名地址;普通保存必须保留无权附件,不能改角色、改名、删除或通过复制路径重新授权。
32
+ 上传响应的 `_UploadProof` 绑定当前租户、用户、字段、记录、私有路径及有效期,只用于首次保存校验,
33
+ 持久化时移除;新文件须携带当前记录/字段上下文上传。历史公有附件需重新私有上传后才能设置角色。
34
+ 完整配置、继承与 MCP 流程见 `../microi-form-engine/SKILL.md`。
35
+
36
+ ### 旧上传返回与接口引擎扩展
37
+
38
+ - 系统设置 `CompatiblePlatformOldVersion` 默认关闭,空值/缺字段也关闭;开启时旧 HTTP 上传
39
+ 保留 `Code/Data`,单文件也统一返回数组(包括 `Multiple=false`),补 `url/type/size/duration/uploading/progress/path/name/id`。
40
+ 图片 `type=image`,名称去掉最后一个扩展名;`url` 必须沿用实际 `Url`,不能把私有文件改拼公有地址。
41
+ - `/api/HDFS/upload`、`/api/Upload`、`/apiengine/platform-hdfs-upload` 使用当前租户上传引擎。
42
+ 只有主库确认完整地址、别名及固定 Key 缺失才执行编译兜底;禁用、StopHttp、拒绝或异常不兜底。
43
+ - `V8.Method.UploadCurrentRequestAsync()` 复用已验证的当前请求文件流,重复调用只上传一次;
44
+ `V8.Method.IsLegacyUploadCompatibilityEnabled()` 读取宿主取得的开关,二者均不接受参数。
45
+ 它们绑定当前租户与选定引擎,普通脚本或嵌套其它引擎不能借用;无请求时使用原有 `V8.Method.Upload`。
46
+ - 返回编排由系统设置应用拥有的 Managed `platform-hdfs-upload` 实现;持久定制使用
47
+ CreateIfMissing `platform-hdfs-upload-hook`,返回 `{Code:1, UploadResult:完整结果}`。
48
+ 先用 `JSON.parse(JSON.stringify(V8.Param.Result))` 转为普通 JS 对象,再判断 `Data` 数组并遍历追加字段;不能直接对 CLR 包装对象使用 `Array.isArray` 或 `length` 分支。
49
+ 系统设置及基础空库包都交付可空字段,但上传引擎只由系统设置包拥有;不携带租户开关值。
50
+ - MCP 复用 `microi_add_field`、`microi_update_field`、`microi_create_engine`、
51
+ `microi_save_engine_code` 和管理员回读;验收分别覆盖真实上传、配置缓存、引擎优先、缺失及错误分支。
52
+
53
+ - `ImgUpload`、`FileUpload`、`RichText` 的“禁止匿名访问”是字段权威策略:`Limit=false` 写公有桶,`Limit=true` 写私有桶。普通用户只要通过当前菜单/表的新增或编辑动作授权,也必须按该字段配置执行;不得按用户等级把全部非超级管理员上传统一改成私有桶。
54
+ - 浏览器上传必须携带 `FormEngineKey + FieldId + SysMenuId`,编辑已有记录再带 `FormDataId`,TableChild 再带父子授权上下文。后端先用 FormEngine 校验动作权限,再从当前租户回读 `diy_field.Component/Config`,用权威 `Limit` 覆盖请求值,并把目录固定为 `ImgUpload→img`、`FileUpload→file`、`RichText→editor`。
55
+ - 客户端 `Limit`、`Path`、字段 Id 和菜单 Id 都只是待验证线索。没有可验证字段上下文的普通交互式上传默认私有并限制到安全一级目录;不能为了恢复公有字段语义而重新信任裸 `Limit=false`。
56
+ - 兼容旧移动端/定制页面时,在普通系统设置 `sys_config.HdfsUploadRules` 配置目录与角色规则,无需逐个改客户端。`Path` 使用区分大小写的租户内相对路径;`RoleIds` 是真实角色 Id 数组,或显式启用 `AllAuthenticated`;`IncludeSubdirectories` 和 `AllowPublic` 默认关闭。只有后端有效角色命中且显式允许公有,才尊重请求 `Limit=false`。规则不是秘密,不必放入后端私有设置,修改权限与服务端判定必须严格受控。
57
+ - 排查目录授权失败时先检查 multipart 是否同时提交 `Path` 和 `path`。客户端只应提交一个 `Path`;后端对相同值归一,对不同值拒绝,不能因重复字段误判而扩大 `HdfsUploadRules`。
58
+ - 后端 v8.2.9+ 的配置 `Path` 支持 `*`(单层任意字符)、`**`(零到多层目录,须独占层级)、`?`、`[abc]`、`[a-z]`、`[!0-9]`/`[^0-9]`、`{a,b}` 候选及组合;优先 `files/{inspection,quality}/**` 这类最小业务前缀,不能为省事直接向全部用户配置 `** + AllowPublic`。只匹配请求目录,不匹配文件名或服务端追加的年月。实际上传路径不接受通配符;保留目录先于 glob 校验,不能用宽泛规则绕过。最多512字符/规则、4层花括号、32候选、4096令牌;有界动态规划和有界语法缓存不得缓存租户授权结果。先更新全部后端节点再配置新语法,精确旧规则仍兼容。
59
+ - 目录规则只扩展无字段上下文上传,不绕过表/菜单/行权限、租户隔离、保留路径、文件类型、内容检测及配额;不能授予访问密钥会话、私有文件读取或应用发布权限。规则通过系统设置共享缓存读取,保存/删除后失效;应用包只发布字段和可空 DDL,禁止附带会覆盖租户规则的配置数据。验收覆盖授权/未授权角色、路径边界、公私桶、禁用用户、跨租户、规则撤销及缓存生效。
60
+ - 老 `ImgUpload/FileUpload` 缺失 `Limit` 时兼容为公有;老 `RichText` 缺失上传配置时默认私有。微信待审图片与裁剪/压缩 `_origin` 原图始终私有,字段公有配置不能放宽这些特殊边界。客户端保存与预览必须以上传响应的实际 `Limit` 为准。
61
+
62
+ ## 交互式图片默认压缩(强制)
63
+
64
+ - `ImgUpload`、PC、UniApp/H5/小程序以及 MCP 普通图片上传在未显式传 `Preview` 时,必须按 `Preview=true` 处理;字段设计器新配置默认开启。只有业务明确要求公开原始画质时才允许显式关闭,不能把“客户端漏传”解释为关闭。
65
+ - 默认展示图目标为不超过约 `500 KB`、最长边不超过 `1920 px`;可按真实用途进一步收紧。压缩必须使用 Windows/Linux/容器一致的跨平台实现,并限制输入字节、像素总量与解码并发,避免超大图触发 OOM。
66
+ - 压缩流程固定为“先将原始字节写入当前租户 HDFS 私有桶的 `_origin` 对象 → 生成压缩展示图 → 写入目标公有/私有位置 → 回读大小与格式”。即使展示图是公有资源,原图也只能保存在私有桶。
67
+ - 压缩、解码或编码失败时必须失败关闭,返回可诊断错误;禁止静默把几十 MB 的未压缩原图发布到公有桶。返回字段中的 `Size` 应表示展示图实际字节数,`OriginalSize` 可用于管理员审计,但业务字段不得保存私有桶真实地址或签名 URL。
68
+ - 历史治理只迁移超过目标体积的图片:先为旧对象补齐私有原图副本,再上传压缩展示图、原子更新全部业务引用并逐条回读。确认没有引用后才删除旧公有对象;批量任务要有清单、检查点、幂等映射和失败重试,不能边扫描边不可逆覆盖。
69
+
70
+ ## 表单引擎图片裁剪(强制)
71
+
72
+ - `ImgUpload.Crop` 支持 `Enabled`、`Mode=free/fixed/select`、`Ratio`、`CustomWidth/CustomHeight`以及 `AllowZoom/AllowRotate/AllowFlip`。`Enabled` 只表示表单用户的默认状态,不得当作裁剪能力总开关;新增/编辑表单把裁剪开关合并到紧凑上传面板,旁边同时显示公有/私有桶、单/多图与最大数量、压缩状态和最大体积。旧字段未配置时该开关默认关闭,但用户仍可主动开启。
73
+ - 裁剪弹层必须同时提供“取消本次上传”“不裁剪直接上传”“应用裁剪并上传”三个不同动作。多图选择按队列逐张处理;直接上传只跳过当前图片的裁剪,不能取消或阻塞后续图片。
74
+ - 多图模式下后端可能为每个单文件请求返回 `Data: [{...}]`,单图模式返回 `Data: {...}`。PC/移动端必须先归一化对象/数组,再按客户端 `uid` 替换上传占位项并生成预览 URL,禁止把接口成功误显示成空列表。
75
+ - 开启裁剪时,前端必须上传最终裁剪图,并在同一 multipart 请求中以 `MicroiOriginalFile` 附带同名、未改动的原图及 `CropEnabled=true`。不得仅传坐标后由后端重演,否则 EXIF 方向、旋转或镜像可能导致前后端结果不一致。
76
+ - 后端必须校验裁剪图与原图一一同名,两者字节数都计入单次限制和每日配额,但原图不计为第二个业务文件。未宣告裁剪却传原图、宣告裁剪却漏传/错配原图都要失败关闭。
77
+ - HDFS 写入顺序固定为“未改动原图写入私有 `_origin` 对象 → 可选压缩裁剪图 → 写入业务展示图”。裁剪原图写入失败时不得发布展示图;即使 `Preview=false`,裁剪原图仍必须私有保留。不得返回或持久化原图真实路径。
78
+
79
+ ## 富文本图片、视频与附件(强制)
80
+
81
+ - `RichText.Limit=false` 只用于需匿名长期访问的官网公告、商品详情等公开正文;内部内容用 `true`。普通用户经表单新增/编辑授权后也按后端回读到的该字段配置选择桶;仅修改请求中的 `Limit` 不能改变策略,客户端必须以上传响应的实际 `Limit` 为准。
82
+ - RichText 分别配置 `Image`、`Video`、`File` 的 `Enabled/MaxSize/MaxCount`;图片另传 `Preview/CompressMaxSize/CompressMaxWidth`,并继续遵循“原图私有、展示图公有或私有”的压缩链路。附件类型白名单用 `File.Accept` 进一步收紧,不能放宽服务端白名单。
83
+ - 私有正文持久化 `/__microi_richtext_private__/...` 稳定对象标识,严禁保存对象存储签名 URL、`OpenPrivateFile` Ticket、DiyToken 或其它会过期的凭据。每次打开记录时携带 `FormEngineKey/FormDataId/FieldId/SysMenuId` 批量换取短效审计代理地址。
84
+ - 私有文件后端授权必须重新校验当前租户、菜单、表、行和 RichText 字段,并确认所请求路径精确存在于 `img/video/source.src` 或 `a.href`;普通上传字段的对象/数组只认 `Path/FilePath/FilePathName`,不得递归把 `Name/Size/Metadata` 等任意标量当作路径。未经当前租户 FileServer 主机权威校验的绝对 HTTP(S) URL 不得等价为本地对象 Key;正文文字、`data-src/data-href`、脚本标签和前缀相似路径都必须失败关闭。
85
+ - 外部匿名页面没有后台记录权限上下文,不能解析私有标识。公开文章应由设计者把 RichText 字段配置为 `Limit=false`,有该表单新增/编辑权限的用户即可按权威配置发布;不得通过延长私有 URL 有效期模拟公开资源。
86
+ - 若业务明确要求撤稿后旧媒体地址立即停止返回字节,使用私有对象和专用批准引用代理:只接受固定业务标识,权威校验当前租户、不可变批准版本与精确媒体引用,读取前后复核发布/许可/撤回状态,限制 MIME/体积并返回 `no-store`;不开放裸路径,不返回签名 URL 或重定向。普通文件短链接口不能代替这种业务撤权能力。
87
+ - 撤回授权与稳定请求键待办先在共享数据库事务提交,再执行 HDFS 删除。未知结果沿用原请求键探查重放,原上传请求摘要保持。删除源对象不等于删除 CDN 缓存;历史公开链接必须有官方失效任务和独立缓存字节读回证据,否则保留未确认状态,不把 `Offline`、隐藏前台或源站 `404` 记作完整撤回。
88
+
89
+ 官网客户端读取私有文件统一调用 `/apiengine/platform-private-file-url`,提交 `FilePathName` 或有界 `FilePathNames`,并按资源类型提供权威定位参数:普通表单字段使用 `FormEngineKey + FormDataId + FieldId + SysMenuId`;用户头像使用 `ResourceKind=UserAvatar + ResourceId=用户Id`;菜单/部门导入模板分别使用 `MenuImportTemplate`、`DeptImportTemplate` 与对应记录 Id。CAD 私有派生预览使用 `ResourceKind=FormFieldDerivedPreview`,除表单四元组外必须同时提交字段中保存的 `OriginalFilePathName` 和单个派生 `FilePathName`;后端只接受同目录同 basename 的 DWG→`_preview.dxf`、STEP/STP→`_preview.stl` 唯一映射,并在对象存在后签名。文件柜对象使用 `ResourceKind=FileManagerObject`,`ResourceId` 必须与单个 `FilePathName` 大小写精确相同,并提交能力探针返回的当前租户权威 `SysMenuId`;此类签名只允许平台超级管理员 DiyToken 会话,访问密钥和普通菜单用户一律拒绝。后端会从权威字段或对象存储重新读取并精确匹配路径;管理员也不能只传裸路径绕过对象引用,普通客户端禁止换取私有文件原始 Byte/Stream。旧 `/api/HDFS/GetPrivateFileUrl` 与 `/api/HDFS/MallFileUrl` 只保留令牌格式兼容并转发同一 Managed 接口,新代码不得继续引用。
90
+
91
+ <!-- microi-progressive:begin -->
92
+ <!-- microi-progressive:chunk id=v8-file-upload-000 sha256=9402f7173710e8d110392f184119154a50187138ab7d698fce0b73b5f12123e2 -->
93
+ ## 核心 API
94
+
95
+ | API | 说明 |
96
+ |-----|------|
97
+ | `V8.FilesByteBase64` | 接收上传时携带的文件字典 `{ FileName: base64 }` |
98
+ | `V8.Method.Upload({...})` | 服务端上传文件到 HDFS(推荐) |
99
+ | `V8.Method.GetPrivateFileUrl({FilePathName})` | 生成私有桶临时访问 URL |
100
+ | `V8.Method.CopyObject({FilePathName,Path,Limit})` | 当前租户同一桶内服务端复制,保留原对象;用于固定入口与版本快照 |
101
+ | `V8.Method.ObjectExist({FilePathName,Limit})` | 检查当前租户公有/私有桶对象是否存在 |
102
+ | `V8.Method.GetObjectSha256({FilePathName,Limit})` | 在服务端流式计算对象原始字节与旧版 Base64 文本的 SHA-256 和字节数;不把对象内容送入 V8 |
103
+ | `V8.Method.ListObjects({Path,Limit,Recursive,Marker,MaxKeys})` | 分页列举当前租户前缀,单页最多 1000 个 |
104
+ | `/apiengine/platform-private-file-url` | 官网 PC/UniApp 按菜单、记录、字段和对象引用换取私有文件短链 |
105
+ | `V8.Http.GetResponse({Url}).RawBytes` | 下载远程文件为字节数组 |
106
+ | 接口返回 `{ FileName, ContentType, FileByteBase64 }` | 接口直接响应文件 |
107
+
108
+ 固定 CDN 应用回填优先使用服务端 `CopyObject`,公有桶复制编译资产、私有桶复制源码;`Limit` 在源与目标间保持一致,`Path` 和 `FilePathName` 均由后端收敛到当前租户。大对象用 `GetObjectSha256` 流式核对原对象和复制目标,公有体验路径仍须从 CDN 独立回读。历史版本目标已存在时须核对字节哈希,发现不同内容立即停止;固定根可在新版本验证后覆盖。`ListObjects` 必须分页并限制到单个应用前缀,不得把这些存储管理原子直接开放为匿名业务接口。大型私有文本的 `GetPrivateFileText` 字符串边界及原字节/最终ZIP验签规则见[已有详细参考](references/progressive-01-公有桶-vs-私有桶.md)。
109
+
110
+ MinIO SDK 7 的复制签名不匹配还可能来自带参数 MIME:SDK 对源 `Content-Type` 签名,却在 HTTP 请求中额外保留 `StringContent` 的默认 MIME。先在独立夹具核对源 MIME、重复头和真实存储返回,再升级后端复制传输修复;不能去掉 `charset`、关闭签名校验或下载后重传来伪造通过。修复后必须同时验证公私桶原始字节、精确 MIME、源元数据及源对象保留,固定入口恢复另覆盖主库权威、真实共享租约、双节点竞争和新进程重跑。取消/超时后保持原请求键,先回读目标再决定是否继续。
111
+
112
+ <!-- /microi-progressive:chunk -->
113
+ <!-- microi-progressive:chunk id=v8-file-upload-001 sha256=d535a333639a9005f5d20f25e36e2753a11835380713c1bb063ae618e6cea4af -->
114
+ ## 第三方数据库附件迁移
115
+
116
+ 当第三方表只保存附件路径时,先用 `microi_inspect_external_database` / `microi_query_external_database` 或 `V8.Dbs.<DbKey>` 查询记录。`microi_import_external_attachment` 允许后端已确认的 `Level >= 9999` 当前用户直接提供 HTTP/HTTPS URL、API 节点可读的本机绝对路径或 UNC 路径。
117
+
118
+ - 导入工具必须显式确认;HTTP、私网、重定向、本机和 UNC 均可访问,但最终能力受 API 服务进程账号、网络、磁盘及对象存储权限约束。
119
+ - 下载与上传使用临时文件和文件流,不经过 Base64;不设固定 20/100 MB 上限,`MaxBytes=0` 或省略表示不设置 MCP 上限,可处理 200/500 MB 或更大文件。
120
+ - 带签名参数或用户凭据的源 URL、鉴权 Header 和本机/UNC 路径不得出现在结果、日志或目标表;脱敏审计只记录来源 SHA-256、类型和字节数,目标字段只保存吾码租户内相对路径。
121
+ - 使用第三方附件 Id/版本作为幂等键,回读目标记录后才标记成功;多节点重投不能重复产生业务附件。
122
+ - 写入目标 `FileUpload` 字段前必须回读其权威 `diy_field.Config.FileUpload.Limit`;目标字段为 `Limit=true` 时,迁移上传也必须使用 `Limit=true` 写入私有桶,不得上传到公有桶后仅靠字段路径伪装为私有文件。
123
+ - 源附件为空、大小为 `0` 或源对象不可读取时,不得创建目标业务记录并把上传字段保存为 `[]`、`'[]'` 或其它空占位值;应仅在迁移账本中记录为跳过或失败,保留来源 Id、原因和可重试状态。
124
+ - 私有附件只有在目标记录回读成功,并使用该记录的权威资源上下文调用 `/apiengine/platform-private-file-url` 取得代理地址,再对该地址执行 `Range: bytes=0-0` 且确认返回 `200/206`、实际读到字节并且不是 JSON 错误后,才允许标记迁移成功;完整验收再核对总字节数或 SHA-256。
125
+ - 批量迁移应落任务状态表并分页处理,失败可重试;不要让 MCP 一次加载整库路径或大文件集合。
126
+
127
+ 可信后端 V8 可用 `V8.Http.GetResponse({ Url: url }).RawBytes` 下载,再用 `System.Convert.ToBase64String` 和 `V8.Method.Upload` 上传。该路径同样必须校验域名、大小、Content-Type、后缀和最终重定向目标。
128
+
129
+ <!-- /microi-progressive:chunk -->
130
+ <!-- microi-progressive:chunk id=v8-file-upload-002 sha256=176e9f91705f20416e177d7dbdbd32fb8f81dff6ba0da1a45b533795d187f0e0 -->
131
+ ## 接收前端上传的文件
132
+
133
+ 前端发起文件上传时,平台自动把文件以 base64 形式注入到 `V8.FilesByteBase64`:
134
+
135
+ ```javascript
136
+ // V8.FilesByteBase64 = { '文件名1.png': 'base64...', '文件名2.pdf': 'base64...' }
137
+ if (!V8.FilesByteBase64) {
138
+ return { Code: 0, Msg: '请上传文件' };
139
+ }
140
+
141
+ var fileNames = Object.keys(V8.FilesByteBase64);
142
+ var firstFile = fileNames[0];
143
+ var firstBase64 = V8.FilesByteBase64[firstFile];
144
+
145
+ // 上传到 HDFS
146
+ var upResult = V8.Method.Upload({
147
+ FilesByteBase64: V8.FilesByteBase64,
148
+ Limit: true, // 业务上传默认私有桶(需临时 URL 访问)
149
+ Preview: false, // true=自动生成预览图
150
+ Path: '/business/orders', // 存储路径前缀
151
+ OsClient: V8.OsClient
152
+ });
153
+
154
+ if (upResult.Code !== 1) return upResult;
155
+
156
+ // upResult.Data = [{ FileName, Path, FullPath, Size, ... }, ...]
157
+ var filePath = upResult.Data[0].Path; // 相对路径,存数据库
158
+ var fullUrl = upResult.Data[0].FullPath; // 完整 URL(公有桶)
159
+ ```
160
+
161
+ ### AI 应用超大资产断点续传
162
+
163
+ Unity `Data`、WASM、Windows 安装包、视频模型等发布资产不得进入 Base64、JSON 或 Jint。`microi_publish_application_directory_stream` 在协议 v3 下与 `@microi.net/cli` 共用同一套传输客户端:单文件大于 128 MiB 时自动切换为原始字节断点续传,小文件继续兼容旧版单请求链路。
164
+
165
+ - 默认分片 16 MiB;5 GiB 文件为 320 片。分片通过 `application/octet-stream` 发送,必须携带精确 `Content-Length` 与 SHA-256。
166
+ - 服务端逐片写入 HDFS 后重新流式回读校验;完成时按顺序合并、再次核对整文件 SHA-256,再生成不可变版本完整性标记。
167
+ - 会话 Id 由租户、应用、版本、路径和文件摘要确定。网络中断或进程重启后先读取远端状态,只续传缺失分片;已成功的相同摘要请求直接幂等返回。
168
+ - 吾码不为协议 v3 设置业务文件/目录字节上限,状态中的 `ApplicationAssetResumableProductSizeLimitBytes=0` 表示没有产品配置上限。每个对象仍受协议技术边界(最多 10000 片、单片最多 1 GiB)、JavaScript 安全整数、对象存储、磁盘、网关和网络条件约束。
169
+ - 该链路只允许通过能力鉴权的当前租户超级管理员,并继续受 `DisableFileUpload` 负向总开关控制;它不是普通用户上传或任意 HDFS 路径写入接口。
170
+ - 每个会话都在 `mci_ai_app_file` 保留审计记录,`StorageScope=ApplicationAssetMultipartSession`。管理员在 **系统引擎 → 超大文件上传记录** 查看状态、阶段、已传字节/分片、进度、心跳、错误和恢复建议;成功、失败和取消记录都不静默删除。
171
+
172
+ ### 普通业务上传的分层限制
173
+
174
+ 以下限制适用于 HTTP、表单、V8、移动端和旧版应用资产单请求,不适用于上面的受信任 v3 断点续传。Token 不是无限上传授权;普通入口必须在解码 Base64、解析图片或调用对象存储前执行服务端校验:
175
+
176
+ 1. **租户业务配置**:有效正数/布尔值按 `sys_osclients` 当前租户 → 代码默认值解析。租户可以按业务需要提高或降低默认值,不要求安装者维护额外环境变量或修改 `appsettings`。
177
+ 2. **平台绝对上限**:最终业务值再与代码内固定灾难保护上限取较小值,租户和安装参数都不能放大。
178
+ 3. **HTTP 解析上限**:Kestrel 请求正文与 Multipart 固定为 2048 MB,普通表单单值固定为 128 MB,是所有租户共享的请求解析硬顶。
179
+ 4. **字段级限制**:前端 `FileUpload` / `ImgUpload` / `RichText` 的 `MaxSize`、`MaxCount` 等只能与当前租户有效值取更小值,不能提高后端上限,也不能替代服务端校验。
180
+
181
+ 业务配置与固定边界:
182
+
183
+ | `sys_osclients` 字段 | 代码默认值 | 平台固定边界 |
184
+ |---|---:|---:|
185
+ | `DisableFileUpload` | `false`(关闭,即允许上传) | — |
186
+ | `FileUploadMaxFileMB` | 100 MB | 1024 MB |
187
+ | `FileUploadMaxRequestMB` | 200 MB | 2048 MB |
188
+ | `FileUploadMaxCount` | 10 | 100 |
189
+ | `FileUploadDailyUserQuotaMB` | 2048 MB | 10 TB |
190
+ | `FileUploadDailyTenantQuotaMB` | 20480 MB | 10 TB |
191
+
192
+ - `sys_osclients` 六个现行字段全部可空;`DisableFileUpload` 空值、无效值或 `0/false` 均表示允许上传,只有 `1/true` 才禁止。旧数据库尚未创建新物理字段时才回退读取 `FileUploadEnabled`,升级后旧正向字段即使为 `0` 也不得覆盖新字段的默认允许语义。`FileUploadMaxRequestMB` 指一次上传所有文件的业务合计大小,不等于 Kestrel HTTP 请求正文上限。
193
+ - 固定灾难保护和 HTTP/Multipart/Form 解析上限不属于安装配置;租户值即使更大也会被这些边界截断。最终单次总量还不能超过帐号或租户的有效日额度,单文件不能超过最终单次总量。
194
+ - `DisableFileUpload=1` 表示禁用当前租户上传,也会阻止 AI 应用资产断点续传;不能把内部发布协议当成绕过开关的后门。普通单请求继续受全局大小硬上限,v3 只移除产品级字节上限;租户配置刷新应走现有 SaaS 引擎重载和共享 Redis 发布订阅,不能依赖单节点内存。
195
+ - 帐号与租户每日额度在共享 Redis 中用单次原子脚本预留,支持多节点;Redis 不可用时失败关闭,不能降级成无限上传。
196
+ - 额度按 UTC 日期统计。为防并发重试绕过限制,后续对象存储失败也不退还已经预留的额度。
197
+ - 每日额度只阻断短期滥用;对象存储必须另外配置租户/桶总容量、账单告警、生命周期与实际用量对账。Redis 计数不能作为长期容量事实源。
198
+ - 标准表单字段上传按后端回读的 `diy_field.Config` 决定桶,不能按帐号等级覆盖字段策略。无字段上下文的普通上传默认只允许平台预定义私有一级目录;管理员可通过 `sys_config.HdfsUploadRules` 按真实角色明确扩展业务目录和公有权限,不能信任客户端自报角色或裸 `Limit:false`。源码、应用产物等仍使用独立受控发布流程。
199
+ - 反向代理、Ingress/IIS 的请求体上限应与进程级 HTTP 解析硬顶协调。字段自身的类型、后缀、大小和数量配置只能进一步收紧当前租户有效值,不能替代平台硬顶。
200
+
201
+ ### 复盘:“当前租户已停用文件上传”
202
+
203
+ - 该提示只表示当前运行环境命中的 `sys_osclients.DisableFileUpload` 被明确设置为 `1/true`(或仍在旧库兼容期且 `FileUploadEnabled=0`)。新字段缺列以外的空值、无效值和新租户都按默认允许,不能先把问题归因于 MinIO/HDFS。
204
+ - 新版响应同时返回 `DataAppend.ErrorType=TenantFileUploadDisabled`、`OsClient`、`ConfigField=DisableFileUpload`、`ExpectedValue=0` 和文档地址;客户端必须保留后端 `Msg/DataAppend`,不能只显示“上传失败”。
205
+ - 处理时先读取当前 API 进程的 `OsClient + OsClientType + OsClientNetwork`,再精确回读同三元组的启用记录并把 `DisableFileUpload` 关闭为 `0`。不能仅按租户名批量覆盖其它网络或环境记录。
206
+ - 保存后等待 SaaS 共享配置重载,再分别验证一个小公有图片和一个小私有文件。只有错误转为 endpoint、bucket、签名或 `Invalid URI` 后,才进入对象存储配置排查。
207
+ - 不要通过删除 Redis 日额度 Key、扩大文件大小上限或改成公有桶来解除租户停用;这些动作与开关无关,还会扩大安全风险。
208
+
209
+ ### AI / MCP 调整租户上传配额
210
+
211
+ 用户明确授权修改某个租户的上传额度时,AI 可以直接使用标准 MCP 完成,不要把应用层提示误判成阿里云 OSS、MinIO 或 S3 的存储配额,也不要先清 Redis:
212
+
213
+ 1. `microi_get_table_data(tableName: "sys_osclients")` 按 `OsClient`、`IsEnable=1` 查询,选择 `Id/OsClientType/OsClientNetwork`、`DisableFileUpload` 和五个现行 `FileUpload*` 配额字段;`FileUploadEnabled` 只用于诊断未升级旧节点。
214
+ 2. 先以当前服务器的 `OsClientType + OsClientNetwork` 收窄到实际生效记录;只有用户明确要求多个环境保持一致时才扩展范围。逐条调用 `microi_update_form_data`,`row` 必须包含 `Id`,并传 `confirmExecution: "sys_osclients"`。
215
+ 3. MB 是存储单位:`20 GB = 20480 MB`。可修改字段为 `DisableFileUpload`、`FileUploadMaxFileMB`、`FileUploadMaxRequestMB`、`FileUploadMaxCount`、`FileUploadDailyUserQuotaMB`、`FileUploadDailyTenantQuotaMB`;不要再写旧 `FileUploadEnabled`。
216
+ 4. 保存后逐条远程回读;FormEngine 会排队重载 SaaS 运行配置,再用真实小文件上传做生效冒烟。只看到 MCP 返回“更新成功”不算验收。
217
+ 5. 提高每日配额保留当天已用计数,剩余额度为新上限减已用量。计数按 UTC 日期,失败上传不退款;除非用户明确授权事故处置,不得删除 Redis 配额 Key。
218
+
219
+ 租户 MCP 只能调整普通业务上传配置;平台固定灾难保护、HTTP/Multipart/Form 解析上限和反向代理上限不能通过 `sys_osclients` 绕过。协议 v3 的原始分片不读取普通单请求大小字段,但仍要求能力鉴权、总开关、版本快照、逐片/整文件哈希和审计。写入 `sys_osclients` 属于控制面操作,只允许当前租户的 `Level >= 9999` 管理身份,并且必须保留 MCP 审计与写后回读。
220
+
221
+ ### UniApp / H5 客户端直传路径规则
222
+
223
+ 移动端通过 `/api/HDFS/UniappUpload` 上传时,前端必须走 `microi.v8.js` 的 `V8.uploadFile`,不要在页面里手写 `uni.uploadFile`。客户端上传的 `Path` 与服务端 `V8.Method.Upload` 示例不同,必须是安全相对路径:
224
+
225
+ - 正确:`mall/pay-proof`、`mall/member/avatar`、`order/proof`
226
+ - 错误:`/mall/pay-proof`、`https://...`、`C:\...`、`../x`、`mall//x`、`~x`
227
+ - multipart 请求不能带 `Content-Type: application/json`,否则后端可能读不到 `Path` 表单字段并返回“移动端文件上传路径不合法!”
228
+ - `OsClient` 只能保留一个规范字段,避免同时提交 `OsClient`、`osclient` 或 query/header/formData 多处互相冲突。
229
+ - 生产 H5 不能只依赖 `uni.uploadFile`。页面从 `uni.chooseImage` 得到的 `tempFiles[0].file`、`tempFiles[0]`、`blob:` / `data:` 临时路径都要传给 `V8.uploadFile`,并设置 `preferFetch:true`;SDK 必须能用 `fetch + FormData` 兜底,否则线上可能报 `未找到 MicroiV8 上传适配器。`。
230
+
231
+ <!-- /microi-progressive:chunk -->
232
+ <!-- microi-progressive:chunk id=v8-file-upload-003 sha256=d02dfde4abd2349cd92de1daee129bc08ba42142b5f6229736588cb3e0dbf43c -->
233
+ ## 跨平台文件同步登录会话
234
+
235
+ 文件柜、文件同步等需要连接另一套 Microi API 的工具,必须把远程平台视为独立登录会话:
236
+
237
+ - 用户必须先完成远程登录,登录成功后显示远程用户名称、帐号、ApiBase、OsClient 和登录状态,并提供明确的退出登录操作。
238
+ - 历史远程连接通过 `mci_` 前缀表保存,并按 `V8.CurrentUser.Id` 做行级隔离;不得把帐号、密码或 Token 放入 `localStorage`。
239
+ - 密码和 Token 只能由受保护的接口引擎写入、读取和清理。数据库必须保存可校验的加密密文,普通 FormEngine 列表不得返回密文字段。
240
+ - 密码和 Token 使用 `V8.Method.ProtectApiEngineSecret/UnprotectApiEngineSecret`,由宿主把密文绑定当前 `OsClient + ApiEngineKey`;不得从已脱敏的 `V8.OsClientModel` 读取 `AuthSecret/DbConn`,也不得使用进程级临时密钥。接口引擎 Key 必须稳定,确保服务重启和应用升级后仍能解密历史连接。
241
+ - 历史连接列表只返回脱敏元数据;一键重连时再按记录 Id 和当前用户读取凭据。删除连接必须同时清除保存的密码和 Token。
242
+ - 远程目标登录后必须调用文件柜能力探针 `mci_file_sync_capability` 检查同步协议版本,并使用其 `Data.FileManagerSysMenuId` 作为目标租户权威文件柜菜单;禁止硬编码发布端菜单 Id。接口不存在、未返回菜单 Id、返回 404/非标准结果或协议版本过低时,提示目标平台更新【文件柜】应用,不得继续同步。
243
+ - 验收至少覆盖:登录成功显示身份、退出后 Token 清空、历史连接一键重连、删除连接、密文落库、服务重启后仍可解密、目标平台缺少能力接口时的升级提示。
244
+
245
+ <!-- /microi-progressive:chunk -->
246
+ <!-- microi-progressive:chunk id=v8-file-upload-004 sha256=8393c7b2b7ce36e563843f43f42e9a06a8cb69fc7b792930912353d846ee769f -->
247
+ ## 空目录标记的受控恢复
248
+
249
+ - 只需移除一个目录占位对象时,使用 `V8.HDFS.DeleteObject({FilePathName:'当前租户目录/', Limit:true/false, EmptyDirectoryOnly:true})` 并等待异步结果。必须是当前租户实际有效超级管理员的可信 V8/DiyToken 会话;不能使用参数中的 `_CurrentUser`、访问密钥或临时维护引擎代替授权。
250
+ - HTTP/MCP 必须使用专用 `/api/HDFS/DeleteEmptyDirectoryMarker` / `microi_delete_empty_directory_marker`,显式选择桶并保留末尾 `/`。旧后端缺路由、非标准结果或成功缺少 `DeletionMode=EmptyDirectoryMarkerOnly` / `VerifiedAbsent=true` 均停止,绝不回退普通递归删除。
251
+ - MCP 默认 dry run;执行 `confirmExecution` 精确等于 `filePathName`。旧 `/api/HDFS/DeleteObject` 不能作为缺少专用路由时的回退。路径拒绝租户根、跨租户、未知绝对前缀、URL、通配符与穿越。原始列表必须完整、大小明确为零,供应商只删精确标记 key,再完整回读;不允许前缀批量删除、忽略续页或把缺失 Size 默认成零。
252
+ - 这不是零字节对象 CAS,也不证明历史 NoPUT。同 key 版本替换没有跨供应商统一条件 DELETE;执行前必须隔离原请求并排除活跃写入。保留历史/当前证据的区别;未知结果先只读回查,不换请求键、不清其它对象、不自动再次 PUT。
253
+
254
+ ## 下载远程文件并存到 HDFS
255
+
256
+ ```javascript
257
+ // 1) 下载远程图片
258
+ var resp = V8.Http.GetResponse({ Url: V8.Param.imageUrl });
259
+ if (resp.StatusCode !== 200) return { Code: 0, Msg: '下载失败' };
260
+
261
+ // 2) 转 base64 后上传到 HDFS
262
+ var base64 = System.Convert.ToBase64String(resp.RawBytes);
263
+ var fileName = V8.Method.NewGuid() + '.png';
264
+
265
+ var upResult = V8.Method.Upload({
266
+ FilesByteBase64: { [fileName]: base64 },
267
+ Limit: false,
268
+ Path: '/imported',
269
+ OsClient: V8.OsClient
270
+ });
271
+
272
+ return upResult;
273
+ ```
274
+
275
+ > 在 V8/Jint 中避免把 `resp.RawBytes` 直接塞进 `FilesByte`;序列化时可能变成数字/浮点数组,导致 `Unexpected token when reading bytes`。更稳的是 `System.Convert.ToBase64String(resp.RawBytes)` 后使用 `FilesByteBase64`。
276
+
277
+ 移动端公开图片优先使用 `.jpg` / `.png` / `.webp`。如果上传 `.svg`,必须确认对象存储返回正确 `Content-Type: image/svg+xml`,否则浏览器可能拦截或不渲染。
278
+
279
+ <!-- /microi-progressive:chunk -->
280
+ ## 详细参考路由(渐进披露)
281
+ 仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。
282
+ - [references/progressive-01-公有桶-vs-私有桶.md](references/progressive-01-公有桶-vs-私有桶.md):公有桶 vs 私有桶;接口直接响应文件(下载/导出);通过 URL 列表批量下载并入库
283
+ - [references/progressive-02-office-文件在线编辑版本号规则.md](references/progressive-02-office-文件在线编辑版本号规则.md):Office 文件在线编辑版本号规则;ImgUpload / FileUpload 字段值兼容规则;安全注意
284
+ <!-- microi-progressive:end -->