@lark-apaas/miaoda-cli 0.1.1-alpha.d20f110 → 0.1.1-alpha.ddba836
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.
- package/dist/api/file/api.js +31 -4
- package/package.json +1 -1
package/dist/api/file/api.js
CHANGED
|
@@ -299,18 +299,28 @@ async function preUpload(appId, req) {
|
|
|
299
299
|
}
|
|
300
300
|
/**
|
|
301
301
|
* 调用 upload callback 拿到对象元数据。
|
|
302
|
-
*
|
|
303
|
-
*
|
|
302
|
+
*
|
|
303
|
+
* 网关 IDL 在 metadata 字段加了 api.response.converter = "decode",正常路径下
|
|
304
|
+
* HTTP 响应里的 metadata 已经被网关从字符串解码成对象;这里两种形态都兼容:
|
|
305
|
+
* - object → 直接当 CallbackObjectVO 用(网关解码场景)
|
|
306
|
+
* - string → JSON.parse 出来用(后端原始形态 / 网关行为变化兜底)
|
|
307
|
+
*
|
|
308
|
+
* 解析失败 / metadata 缺失时返回空对象,由 uploadFile 用本地兜底字段填充。
|
|
304
309
|
*/
|
|
305
310
|
async function uploadCallback(appId, req) {
|
|
306
311
|
const url = `/v1/storage/app/${encodeURIComponent(appId)}/object/callback`;
|
|
307
312
|
const body = await (0, client_1.doPost)(url, req, { errorContext: "upload callback" });
|
|
308
313
|
const data = extractEnvelope(body);
|
|
309
|
-
|
|
314
|
+
const metadata = data.metadata;
|
|
315
|
+
if (!metadata) {
|
|
310
316
|
return {};
|
|
311
317
|
}
|
|
318
|
+
if (typeof metadata === "object") {
|
|
319
|
+
return metadata;
|
|
320
|
+
}
|
|
321
|
+
// string 形态兜底
|
|
312
322
|
try {
|
|
313
|
-
return JSON.parse(
|
|
323
|
+
return JSON.parse(metadata);
|
|
314
324
|
}
|
|
315
325
|
catch (err) {
|
|
316
326
|
(0, logger_1.debug)(`upload callback metadata json parse failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -341,11 +351,15 @@ async function uploadFile(opts) {
|
|
|
341
351
|
// 这里复制到独立 ArrayBuffer 以满足 lib.dom.d.ts 的 BodyInit 约束
|
|
342
352
|
const ab = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
|
|
343
353
|
const uploadStart = Date.now();
|
|
354
|
+
// Content-Disposition 用 attachment + filename 编码原始文件名。TOS 会把这个
|
|
355
|
+
// header 作为对象 metadata 存住,服务端 callback 阶段 HeadObject 读回并解析
|
|
356
|
+
// filename 写入 DB。我们要不传 header,服务端走兜底会把 storage key 当文件名。
|
|
344
357
|
const res = await fetch(pre.uploadURL, {
|
|
345
358
|
method: "PUT",
|
|
346
359
|
headers: {
|
|
347
360
|
"Content-Type": opts.contentType,
|
|
348
361
|
"Content-Length": String(opts.fileSize),
|
|
362
|
+
"Content-Disposition": `attachment; filename="${sanitizeFileName(opts.fileName)}"`,
|
|
349
363
|
},
|
|
350
364
|
body: ab,
|
|
351
365
|
});
|
|
@@ -378,6 +392,8 @@ async function uploadFile(opts) {
|
|
|
378
392
|
? (metadata.filePath.startsWith("/") ? metadata.filePath : "/" + metadata.filePath)
|
|
379
393
|
: (opts.remotePath ?? "/" + opts.fileName);
|
|
380
394
|
const result = {
|
|
395
|
+
// 优先取服务端 ObjectVO.name(来自 PUT 时带的 Content-Disposition),
|
|
396
|
+
// 与后续 file ls / file stat 返回的展示名保持一致;缺失时降级用本地 fileName。
|
|
381
397
|
file_name: metadata.name ?? opts.fileName,
|
|
382
398
|
path,
|
|
383
399
|
size: metadata.metadata?.contentLength ?? opts.fileSize,
|
|
@@ -388,6 +404,17 @@ async function uploadFile(opts) {
|
|
|
388
404
|
}
|
|
389
405
|
return result;
|
|
390
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* 把文件名清理成可安全放进 Content-Disposition `filename="..."` 的形态。
|
|
409
|
+
* 与 fullstack-plugin 的 sanitizeFileName 行为一致:
|
|
410
|
+
* 1. 去掉对 TOS / 文件系统不友好的字符 [: " \ / * ? < > | , ;]
|
|
411
|
+
* 2. encodeURIComponent 把非 ASCII(中文等)做百分号编码,保证 header 合法
|
|
412
|
+
* 3. 处理后为空时退回 "download_file" 兜底
|
|
413
|
+
*/
|
|
414
|
+
function sanitizeFileName(fileName) {
|
|
415
|
+
const illegalChars = /[:"\\/*?<>|,;]/g;
|
|
416
|
+
return encodeURIComponent(fileName.replace(illegalChars, "")) || "download_file";
|
|
417
|
+
}
|
|
391
418
|
// ── 预签下载 URL ──
|
|
392
419
|
/**
|
|
393
420
|
* 获取预签下载 URL。
|