[P0] COS 预签名 URL q-sign-time 结束时间戳被截断,iOS 端文件下载全部 AccessDenied #69

Closed
opened 2026-06-03 22:56:25 +08:00 by wangdl · 3 comments
Owner

现象

iOS App 请求下载知识点对应的 .md 文件时,COS 返回 403 AccessDenied。

根因

后端生成腾讯云 COS 预签名 URL 时 q-sign-time 参数的结束时间戳被截断。

错误: q-sign-time=1779893650;1779(结束时间应为完整时间戳)

排查方向

  1. COS 签名生成代码中 q-sign-time 的字符串拼接逻辑
  2. 数据库存储 URL 时是否有字段长度限制导致截断

关联网址

https://git.longde.cloud/wangdl/ios-projects

## 现象 iOS App 请求下载知识点对应的 .md 文件时,COS 返回 403 AccessDenied。 ## 根因 后端生成腾讯云 COS 预签名 URL 时 q-sign-time 参数的结束时间戳被截断。 错误: q-sign-time=1779893650;1779(结束时间应为完整时间戳) ## 排查方向 1. COS 签名生成代码中 q-sign-time 的字符串拼接逻辑 2. 数据库存储 URL 时是否有字段长度限制导致截断 ## 关联网址 https://git.longde.cloud/wangdl/ios-projects
Author
Owner

修复汇报 (2026-06-05)

根因

COS 预签名 URL 通过 cos-nodejs-sdk-v5 在创建知识点时生成了 24 小时有效期签名,存入 DB 的 content 字段。签名过期后 COS 返回 403 AccessDenied

修复方案

KnowledgeItemsService.findById(即 GET /knowledge-items/:id)中新增 enrichItem() 方法:

  1. 检测 content 是否是 COS URL(含 .cos.myqcloud.com
  2. 解析 URL 提取 objectKey
  3. 通过 StorageService.getDownloadUrl() 生成新的 7 天有效签名 URL
  4. 列表接口 findByKnowledgeBaseId 不做刷新(避免 N 次 COS API 调用)

修改文件

  • src/modules/knowledge-items/knowledge-items.service.ts — 新增 enrichItem()
  • src/modules/knowledge-items/knowledge-items.module.ts — 导入 StorageModule

iOS 端配合

iOS KnowledgeDetailViewModel.load() 已通过 KnowledgeItemService.shared.detail(id:) 重新获取知识点,拿到新鲜 URL 后再下载。

状态

代码已完成,待部署到服务器验证。

## 修复汇报 (2026-06-05) ### 根因 COS 预签名 URL 通过 `cos-nodejs-sdk-v5` 在创建知识点时生成了 24 小时有效期签名,存入 DB 的 `content` 字段。签名过期后 COS 返回 `403 AccessDenied`。 ### 修复方案 在 `KnowledgeItemsService.findById`(即 `GET /knowledge-items/:id`)中新增 `enrichItem()` 方法: 1. 检测 `content` 是否是 COS URL(含 `.cos.` 和 `myqcloud.com`) 2. 解析 URL 提取 `objectKey` 3. 通过 `StorageService.getDownloadUrl()` 生成新的 7 天有效签名 URL 4. 列表接口 `findByKnowledgeBaseId` 不做刷新(避免 N 次 COS API 调用) ### 修改文件 - `src/modules/knowledge-items/knowledge-items.service.ts` — 新增 `enrichItem()` - `src/modules/knowledge-items/knowledge-items.module.ts` — 导入 `StorageModule` ### iOS 端配合 iOS `KnowledgeDetailViewModel.load()` 已通过 `KnowledgeItemService.shared.detail(id:)` 重新获取知识点,拿到新鲜 URL 后再下载。 ### 状态 ✅ 代码已完成,待部署到服务器验证。
Author
Owner

Bug 修复 (2026-06-06)

问题

enrichItem()enriched.sourceType = enriched.sourceType || headInfo.contentType 将 COS 返回的 MIME 类型(text/markdownapplication/pdf)写入了 sourceType 字段,而 sourceType 应该是 markdownpdf 等简洁值。

修复

移除 sourceType 的 MIME type 覆盖逻辑。sourceType 由 detectSourceType() 在创建时自动检测,不需要 enrichItem 再设置。fileSize 仍正常填充。

状态

已修复。

## Bug 修复 (2026-06-06) ### 问题 `enrichItem()` 中 `enriched.sourceType = enriched.sourceType || headInfo.contentType` 将 COS 返回的 MIME 类型(`text/markdown`、`application/pdf`)写入了 `sourceType` 字段,而 sourceType 应该是 `markdown`、`pdf` 等简洁值。 ### 修复 移除 `sourceType` 的 MIME type 覆盖逻辑。sourceType 由 `detectSourceType()` 在创建时自动检测,不需要 enrichItem 再设置。fileSize 仍正常填充。 ### 状态 ✅ 已修复。
Author
Owner

优化 (2026-06-06)

问题

enrichItem() 在 COS 调用失败时静默 catch {} 吞错,排查困难。

修复

改用 catch (err) + Logger.warn() 记录错误信息。

状态

已修复。

## 优化 (2026-06-06) ### 问题 `enrichItem()` 在 COS 调用失败时静默 `catch {}` 吞错,排查困难。 ### 修复 改用 `catch (err)` + `Logger.warn()` 记录错误信息。 ### 状态 ✅ 已修复。
Sign in to join this conversation.
No description provided.