首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >COS 分块上传完成判定:为什么收到 HTTP 200 还不能宣布成功

COS 分块上传完成判定:为什么收到 HTTP 200 还不能宣布成功

原创
作者头像
一只特立独行的兔先生
发布于 2026-10-20 14:33:09
发布于 2026-10-20 14:33:09
1020
举报

结论:收到 200 不等于分块合并完成

给 COS 编写上传适配器时,一个容易忽略的边界是:Complete Multipart Upload 的 HTTP 状态与业务结果不是同一层证据。官方文档说明,合并可能需要数分钟,开始合并时会先返回 200,通过 Transfer-Encoding: chunked 继续传输响应,最终结果需要读取完整响应体才能判断。

因此,不能在拿到响应头时就把数据库中的文件标记为“可下载”。正确顺序是:完整读取响应、解析 XML、识别成功或错误结果,再提交业务状态。本文讨论直接调用 REST API 的适配层;使用官方 SDK 时,应先确认 SDK 已经处理的语义,不要重复实现一套协议解析。

一、把三个时刻分开

  • 收到响应头:服务端开始响应,但合并结果尚未确定。
  • 收到完整响应体:有了判断结果的输入,但仍需解析。
  • 识别成功结果:XML 根节点为 CompleteMultipartUploadResult,且必要字段符合预期,才进入成功分支。

如果读取中途连接断开,结果应是“未知”,不是“明确失败”。服务端可能已经完成合并,只是客户端没有收到最终响应。把未知直接当失败并重新启动整套上传,既可能浪费流量,也可能覆盖同名对象。

业务状态可以采用 UPLOADING → COMPLETING → AVAILABLE,其中网络中断进入 UNKNOWN,由后续核查决定去向。这是应用设计建议,不是 COS 返回的枚举值。

二、先把响应读取完整,再判断 XML

下面是浏览器或现代 JavaScript 运行环境可运行的解析函数。输入 xml 必须来自已完整读取的响应体;DOMParser 在普通 Node.js 中不是内置对象,服务端应替换为经过维护的 XML 解析库,并禁用外部实体等不需要的能力。

代码语言:javascript
复制
function classifyComplete(status, xml) {
  const doc = new DOMParser().parseFromString(
    xml, 'application/xml'
  );
  if (doc.getElementsByTagName('parsererror').length) {
    return { state: 'UNKNOWN', reason: 'invalid XML' };
  }
  const root = doc.documentElement;
  const field = name =>
    Array.from(root.children)
      .find(e => e.localName === name)?.textContent ?? '';

  if (root.localName === 'Error') {
    return {
      state: 'ERROR',
      code: field('Code'),
      requestId: field('RequestId')
    };
  }
  if (status !== 200 ||
      root.localName !== 'CompleteMultipartUploadResult') {
    return { state: 'UNKNOWN', reason: 'unexpected response' };
  }
  if (!field('ETag') || !field('Bucket') || !field('Key')) {
    return { state: 'UNKNOWN', reason: 'missing result fields' };
  }
  return {
    state: 'SUCCESS',
    bucket: field('Bucket'),
    key: field('Key'),
    etag: field('ETag')
  };
}

这里用 XML 根节点而不是字符串包含判断,避免错误描述中出现成功节点名称就误判。返回 SUCCESS 后,还应在调用方核对桶与对象键是否属于本次上传;对象键含特殊字符时,按接口约定处理编码,不要盲目做多次 URL 解码。

ERROR 只表示收到了明确错误响应,不表示所有错误都能自动重试;是否重试要结合错误码。空正文、截断 XML、代理返回 HTML 等情况进入 UNKNOWN,不能伪装成上传成功。

三、不访问云资源也能检查关键分支

把上面的函数复制到浏览器开发者工具,再运行以下代码。这些是构造的协议样例,不是线上上传结果;目的是检查适配层是否仅凭 200 判成功。

代码语言:javascript
复制
const success = '<CompleteMultipartUploadResult>' +
  '<Bucket>demo-1250000000</Bucket><Key>report.csv</Key>' +
  '<ETag>example-etag</ETag></CompleteMultipartUploadResult>';
const error = '<Error><Code>InternalError</Code>' +
  '<RequestId>example-request</RequestId></Error>';
const cases = [
  [200, success, 'SUCCESS'],
  [200, error, 'ERROR'],
  [200, '<CompleteMultipartUploadResult>', 'UNKNOWN'],
  [502, '<html>gateway failure</html>', 'UNKNOWN'],
  [200, '<CompleteMultipartUploadResult/>', 'UNKNOWN']
];
for (const [status, body, expected] of cases) {
  const actual = classifyComplete(status, body).state;
  if (actual !== expected) {
    throw new Error(`${expected} != ${actual}`);
  }
}
console.log('5 cases passed');

集成验收还应覆盖“响应读取到一半断开”。这发生在解析函数之前,调用方必须捕获读取异常并保留未知状态,不能因为函数没返回错误就默认成功。

四、未知结果的处理边界

提交合并前,持久化本次桶、对象键、UploadId、分块编号及对应 ETag,并关联业务上传记录。提交清单使用各分块上传返回的 ETag,按 PartNumber 升序组织;不要临时猜测或重算这些标识。

发生超时后,先核查目标对象。只看到对象存在仍不足以证明本次合并成功:同名键可能有历史对象或并发写入。使用每次上传独立的对象键,可以减少这种歧义;同时结合预期长度、校验信息与业务记录核对。不要把分块对象的 ETag 无条件当作整个文件的 MD5。

日志保留阶段、HTTP 状态、错误码和请求标识即可,避免记录签名头、临时密钥与完整授权 URL。客户端超时阈值应覆盖响应体读取,而不仅是建立连接;盲目调大超时不能替代未知结果核查。

最终原则很简单:响应头是传输证据,完整 XML 是接口结果证据,对象核查是异常恢复证据。三者各司其职,才能避免出现“页面提示上传完成,下载时却找不到文件”的状态错位。

参考资料

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 结论:收到 200 不等于分块合并完成
  • 一、把三个时刻分开
  • 二、先把响应读取完整,再判断 XML
  • 三、不访问云资源也能检查关键分支
  • 四、未知结果的处理边界
  • 参考资料
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档