feat: 腾讯云 COS 迁移至火山引擎 TOS 对象存储
签名算法从 COS HMAC-SHA1 改为 TOS V4 HMAC-SHA256, 更新前后端上传凭证字段、配置项、备份脚本和文档。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
eba9ce8e60
commit
3a444864ac
@ -106,9 +106,9 @@ useEffect(() => { loadData() }, [loadData])
|
|||||||
- 配置项:`AI_PROVIDER`, `AI_API_KEY`, `AI_API_BASE_URL`
|
- 配置项:`AI_PROVIDER`, `AI_API_KEY`, `AI_API_BASE_URL`
|
||||||
|
|
||||||
### 文件上传
|
### 文件上传
|
||||||
- 腾讯云 COS 直传,前端通过 `useOSSUpload` hook 处理
|
- 火山引擎 TOS 直传,前端通过 `useOSSUpload` hook 处理
|
||||||
- 流程:`api.getUploadPolicy()` → POST 到 COS → `api.fileUploaded()` 回调
|
- 流程:`api.getUploadPolicy()` → POST 到 TOS → `api.fileUploaded()` 回调
|
||||||
- COS 签名:HMAC-SHA1,字段包括 `q-sign-algorithm`、`q-ak`、`q-key-time`、`q-signature`、`policy`
|
- TOS V4 签名:HMAC-SHA256,字段包括 `x-tos-algorithm`、`x-tos-credential`、`x-tos-date`、`x-tos-signature`、`policy`
|
||||||
|
|
||||||
### 实时推送
|
### 实时推送
|
||||||
- SSE (Server-Sent Events),端点 `/api/v1/sse/events`
|
- SSE (Server-Sent Events),端点 `/api/v1/sse/events`
|
||||||
|
|||||||
@ -30,12 +30,13 @@ AI_PROVIDER=oneapi
|
|||||||
AI_API_KEY=
|
AI_API_KEY=
|
||||||
AI_API_BASE_URL=
|
AI_API_BASE_URL=
|
||||||
|
|
||||||
# --- 腾讯云 COS ---
|
# --- 火山引擎 TOS ---
|
||||||
COS_SECRET_ID=
|
TOS_ACCESS_KEY_ID=
|
||||||
COS_SECRET_KEY=
|
TOS_SECRET_ACCESS_KEY=
|
||||||
COS_REGION=ap-guangzhou
|
TOS_REGION=cn-beijing
|
||||||
COS_BUCKET_NAME=miaosi-files-1250000000
|
TOS_BUCKET_NAME=miaosi-files
|
||||||
COS_CDN_DOMAIN=
|
TOS_ENDPOINT=
|
||||||
|
TOS_CDN_DOMAIN=
|
||||||
|
|
||||||
# --- 邮件 SMTP ---
|
# --- 邮件 SMTP ---
|
||||||
SMTP_HOST=
|
SMTP_HOST=
|
||||||
|
|||||||
@ -21,11 +21,11 @@ class UploadPolicyRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class UploadPolicyResponse(BaseModel):
|
class UploadPolicyResponse(BaseModel):
|
||||||
"""COS 直传凭证响应"""
|
"""TOS 直传凭证响应"""
|
||||||
q_sign_algorithm: str
|
x_tos_algorithm: str
|
||||||
q_ak: str
|
x_tos_credential: str
|
||||||
q_key_time: str
|
x_tos_date: str
|
||||||
q_signature: str
|
x_tos_signature: str
|
||||||
policy: str
|
policy: str
|
||||||
host: str
|
host: str
|
||||||
dir: str
|
dir: str
|
||||||
@ -56,9 +56,9 @@ async def get_upload_policy(
|
|||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
获取 COS 直传凭证
|
获取 TOS 直传凭证
|
||||||
|
|
||||||
前端使用此凭证直接上传文件到腾讯云 COS,无需经过后端。
|
前端使用此凭证直接上传文件到火山引擎 TOS,无需经过后端。
|
||||||
|
|
||||||
文件类型说明:
|
文件类型说明:
|
||||||
- script: 脚本文档 (docx, pdf, xlsx, txt, pptx)
|
- script: 脚本文档 (docx, pdf, xlsx, txt, pptx)
|
||||||
@ -92,10 +92,10 @@ async def get_upload_policy(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return UploadPolicyResponse(
|
return UploadPolicyResponse(
|
||||||
q_sign_algorithm=policy["q_sign_algorithm"],
|
x_tos_algorithm=policy["x_tos_algorithm"],
|
||||||
q_ak=policy["q_ak"],
|
x_tos_credential=policy["x_tos_credential"],
|
||||||
q_key_time=policy["q_key_time"],
|
x_tos_date=policy["x_tos_date"],
|
||||||
q_signature=policy["q_signature"],
|
x_tos_signature=policy["x_tos_signature"],
|
||||||
policy=policy["policy"],
|
policy=policy["policy"],
|
||||||
host=policy["host"],
|
host=policy["host"],
|
||||||
dir=policy["dir"],
|
dir=policy["dir"],
|
||||||
|
|||||||
@ -32,12 +32,13 @@ class Settings(BaseSettings):
|
|||||||
AI_API_KEY: str = "" # 中转服务商的 API Key
|
AI_API_KEY: str = "" # 中转服务商的 API Key
|
||||||
AI_API_BASE_URL: str = "" # 中转服务商的 Base URL,如 https://api.oneinall.ai/v1
|
AI_API_BASE_URL: str = "" # 中转服务商的 Base URL,如 https://api.oneinall.ai/v1
|
||||||
|
|
||||||
# 腾讯云 COS 配置
|
# 火山引擎 TOS 配置
|
||||||
COS_SECRET_ID: str = ""
|
TOS_ACCESS_KEY_ID: str = ""
|
||||||
COS_SECRET_KEY: str = ""
|
TOS_SECRET_ACCESS_KEY: str = ""
|
||||||
COS_REGION: str = "ap-guangzhou"
|
TOS_REGION: str = "cn-beijing"
|
||||||
COS_BUCKET_NAME: str = "miaosi-files-1250000000"
|
TOS_BUCKET_NAME: str = "miaosi-files"
|
||||||
COS_CDN_DOMAIN: str = "" # CDN 自定义域名,空则用 COS 源站
|
TOS_ENDPOINT: str = "" # 自定义 Endpoint,空则用默认 tos-cn-{region}.volces.com
|
||||||
|
TOS_CDN_DOMAIN: str = "" # CDN 自定义域名,空则用 TOS 源站
|
||||||
|
|
||||||
# 邮件 SMTP
|
# 邮件 SMTP
|
||||||
SMTP_HOST: str = ""
|
SMTP_HOST: str = ""
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
腾讯云 COS 服务 — 表单直传签名
|
火山引擎 TOS (Volcengine Object Storage) 服务 — 表单直传签名 (V4)
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
import hmac
|
import hmac
|
||||||
@ -7,7 +7,7 @@ import base64
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
|
|
||||||
|
|
||||||
@ -17,90 +17,94 @@ def generate_upload_policy(
|
|||||||
upload_dir: Optional[str] = None,
|
upload_dir: Optional[str] = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
生成前端直传 COS 所需的 Policy 和签名
|
生成前端直传 TOS 所需的 Policy 和签名 (V4 HMAC-SHA256)
|
||||||
|
|
||||||
COS 表单直传签名流程:
|
TOS 表单直传签名流程 (PostObject):
|
||||||
1. key_time = "{start_time};{end_time}"
|
1. 构建 policy JSON → Base64 编码
|
||||||
2. sign_key = HMAC-SHA1(secret_key, key_time)
|
2. 派生签名密钥: kDate → kRegion → kService → kSigning
|
||||||
3. policy JSON → Base64 编码
|
3. signature = HMAC-SHA256(kSigning, policy_base64)
|
||||||
4. string_to_sign = SHA1(policy_base64)
|
|
||||||
5. signature = HMAC-SHA1(sign_key, string_to_sign)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
{
|
{
|
||||||
"q_sign_algorithm": "sha1",
|
"x_tos_algorithm": "TOS4-HMAC-SHA256",
|
||||||
"q_ak": "SecretId",
|
"x_tos_credential": "AKIDxxxx/20260210/cn-beijing/tos/request",
|
||||||
"q_key_time": "{start};{end}",
|
"x_tos_date": "20260210T120000Z",
|
||||||
"q_signature": "...",
|
"x_tos_signature": "...",
|
||||||
"policy": "base64 encoded policy",
|
"policy": "base64 encoded policy",
|
||||||
"host": "https://bucket.cos.region.myqcloud.com",
|
"host": "https://bucket.tos-cn-beijing.volces.com",
|
||||||
"dir": "uploads/2026/02/",
|
"dir": "uploads/2026/02/",
|
||||||
"expire": 1234567890,
|
"expire": 1234567890,
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if not settings.COS_SECRET_ID or not settings.COS_SECRET_KEY:
|
if not settings.TOS_ACCESS_KEY_ID or not settings.TOS_SECRET_ACCESS_KEY:
|
||||||
raise ValueError("COS 配置未设置")
|
raise ValueError("TOS 配置未设置")
|
||||||
|
|
||||||
# 计算时间范围
|
# 计算时间
|
||||||
start_time = int(time.time())
|
now_utc = datetime.now(timezone.utc)
|
||||||
end_time = start_time + expire_seconds
|
date_stamp = now_utc.strftime("%Y%m%d") # 20260210
|
||||||
|
tos_date = now_utc.strftime("%Y%m%dT%H%M%SZ") # 20260210T120000Z
|
||||||
|
expire_time = int(time.time()) + expire_seconds
|
||||||
|
expiration = datetime.fromtimestamp(expire_time, tz=timezone.utc).strftime(
|
||||||
|
"%Y-%m-%dT%H:%M:%S.000Z"
|
||||||
|
)
|
||||||
|
|
||||||
# key_time: "{start};{end}"
|
# Credential scope
|
||||||
key_time = f"{start_time};{end_time}"
|
region = settings.TOS_REGION
|
||||||
|
credential = f"{settings.TOS_ACCESS_KEY_ID}/{date_stamp}/{region}/tos/request"
|
||||||
|
|
||||||
# 默认上传目录:uploads/年/月/
|
# 默认上传目录:uploads/年/月/
|
||||||
if upload_dir is None:
|
if upload_dir is None:
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
upload_dir = f"uploads/{now.year}/{now.month:02d}/"
|
upload_dir = f"uploads/{now.year}/{now.month:02d}/"
|
||||||
|
|
||||||
# 1. sign_key = HMAC-SHA1(secret_key, key_time)
|
# 1. 构建 Policy
|
||||||
sign_key = hmac.new(
|
|
||||||
settings.COS_SECRET_KEY.encode(),
|
|
||||||
key_time.encode(),
|
|
||||||
hashlib.sha1,
|
|
||||||
).hexdigest()
|
|
||||||
|
|
||||||
# 2. 构建 Policy(COS 表单上传 Policy 格式)
|
|
||||||
policy_dict = {
|
policy_dict = {
|
||||||
"expiration": datetime.utcfromtimestamp(end_time).strftime(
|
"expiration": expiration,
|
||||||
"%Y-%m-%dT%H:%M:%S.000Z"
|
|
||||||
),
|
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{"bucket": settings.COS_BUCKET_NAME},
|
{"bucket": settings.TOS_BUCKET_NAME},
|
||||||
["starts-with", "$key", upload_dir],
|
["starts-with", "$key", upload_dir],
|
||||||
{"q-sign-algorithm": "sha1"},
|
{"x-tos-algorithm": "TOS4-HMAC-SHA256"},
|
||||||
{"q-ak": settings.COS_SECRET_ID},
|
{"x-tos-credential": credential},
|
||||||
{"q-sign-time": key_time},
|
{"x-tos-date": tos_date},
|
||||||
["content-length-range", 0, max_size_mb * 1024 * 1024],
|
["content-length-range", 0, max_size_mb * 1024 * 1024],
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
# 3. Base64 编码 Policy
|
# 2. Base64 编码 Policy
|
||||||
policy_json = json.dumps(policy_dict)
|
policy_json = json.dumps(policy_dict)
|
||||||
policy_base64 = base64.b64encode(policy_json.encode()).decode()
|
policy_base64 = base64.b64encode(policy_json.encode()).decode()
|
||||||
|
|
||||||
# 4. string_to_sign = SHA1(policy_base64)
|
# 3. 派生签名密钥 (V4 Signing Key)
|
||||||
string_to_sign = hashlib.sha1(policy_base64.encode()).hexdigest()
|
k_date = hmac.new(
|
||||||
|
f"TOS4{settings.TOS_SECRET_ACCESS_KEY}".encode(),
|
||||||
|
date_stamp.encode(),
|
||||||
|
hashlib.sha256,
|
||||||
|
).digest()
|
||||||
|
|
||||||
# 5. signature = HMAC-SHA1(sign_key, string_to_sign)
|
k_region = hmac.new(k_date, region.encode(), hashlib.sha256).digest()
|
||||||
|
k_service = hmac.new(k_region, b"tos", hashlib.sha256).digest()
|
||||||
|
k_signing = hmac.new(k_service, b"request", hashlib.sha256).digest()
|
||||||
|
|
||||||
|
# 4. signature = HMAC-SHA256(kSigning, policy_base64)
|
||||||
signature = hmac.new(
|
signature = hmac.new(
|
||||||
sign_key.encode(),
|
k_signing,
|
||||||
string_to_sign.encode(),
|
policy_base64.encode(),
|
||||||
hashlib.sha1,
|
hashlib.sha256,
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
# 构建 Host
|
# 构建 Host
|
||||||
host = f"https://{settings.COS_BUCKET_NAME}.cos.{settings.COS_REGION}.myqcloud.com"
|
endpoint = settings.TOS_ENDPOINT or f"tos-cn-{region}.volces.com"
|
||||||
|
host = f"https://{settings.TOS_BUCKET_NAME}.{endpoint}"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"q_sign_algorithm": "sha1",
|
"x_tos_algorithm": "TOS4-HMAC-SHA256",
|
||||||
"q_ak": settings.COS_SECRET_ID,
|
"x_tos_credential": credential,
|
||||||
"q_key_time": key_time,
|
"x_tos_date": tos_date,
|
||||||
"q_signature": signature,
|
"x_tos_signature": signature,
|
||||||
"policy": policy_base64,
|
"policy": policy_base64,
|
||||||
"host": host,
|
"host": host,
|
||||||
"dir": upload_dir,
|
"dir": upload_dir,
|
||||||
"expire": end_time,
|
"expire": expire_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -108,18 +112,19 @@ def get_file_url(file_key: str) -> str:
|
|||||||
"""
|
"""
|
||||||
获取文件的访问 URL
|
获取文件的访问 URL
|
||||||
|
|
||||||
优先使用 CDN 域名,否则用 COS 源站域名。
|
优先使用 CDN 域名,否则用 TOS 源站域名。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_key: 文件在 COS 中的 key,如 "uploads/2026/02/video.mp4"
|
file_key: 文件在 TOS 中的 key,如 "uploads/2026/02/video.mp4"
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
完整的访问 URL
|
完整的访问 URL
|
||||||
"""
|
"""
|
||||||
if settings.COS_CDN_DOMAIN:
|
if settings.TOS_CDN_DOMAIN:
|
||||||
host = settings.COS_CDN_DOMAIN
|
host = settings.TOS_CDN_DOMAIN
|
||||||
else:
|
else:
|
||||||
host = f"https://{settings.COS_BUCKET_NAME}.cos.{settings.COS_REGION}.myqcloud.com"
|
endpoint = settings.TOS_ENDPOINT or f"tos-cn-{settings.TOS_REGION}.volces.com"
|
||||||
|
host = f"https://{settings.TOS_BUCKET_NAME}.{endpoint}"
|
||||||
|
|
||||||
# 确保 host 以 https:// 开头
|
# 确保 host 以 https:// 开头
|
||||||
if not host.startswith("http"):
|
if not host.startswith("http"):
|
||||||
@ -139,22 +144,23 @@ def parse_file_key_from_url(url: str) -> str:
|
|||||||
从完整 URL 解析出文件 key
|
从完整 URL 解析出文件 key
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
url: 完整的 COS URL
|
url: 完整的 TOS URL
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
文件 key
|
文件 key
|
||||||
"""
|
"""
|
||||||
# 尝试移除 CDN 域名
|
# 尝试移除 CDN 域名
|
||||||
if settings.COS_CDN_DOMAIN:
|
if settings.TOS_CDN_DOMAIN:
|
||||||
cdn = settings.COS_CDN_DOMAIN.rstrip("/")
|
cdn = settings.TOS_CDN_DOMAIN.rstrip("/")
|
||||||
if not cdn.startswith("http"):
|
if not cdn.startswith("http"):
|
||||||
cdn = f"https://{cdn}"
|
cdn = f"https://{cdn}"
|
||||||
if url.startswith(cdn):
|
if url.startswith(cdn):
|
||||||
return url[len(cdn):].lstrip("/")
|
return url[len(cdn):].lstrip("/")
|
||||||
|
|
||||||
# 尝试移除 COS 源站域名
|
# 尝试移除 TOS 源站域名
|
||||||
cos_host = f"https://{settings.COS_BUCKET_NAME}.cos.{settings.COS_REGION}.myqcloud.com"
|
endpoint = settings.TOS_ENDPOINT or f"tos-cn-{settings.TOS_REGION}.volces.com"
|
||||||
if url.startswith(cos_host):
|
tos_host = f"https://{settings.TOS_BUCKET_NAME}.{endpoint}"
|
||||||
return url[len(cos_host):].lstrip("/")
|
if url.startswith(tos_host):
|
||||||
|
return url[len(tos_host):].lstrip("/")
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ===========================
|
# ===========================
|
||||||
# PostgreSQL 每日备份脚本
|
# PostgreSQL 每日备份脚本
|
||||||
# 备份到本地 + 上传到腾讯云 COS
|
# 备份到本地 + 上传到火山引擎 TOS
|
||||||
# ===========================
|
# ===========================
|
||||||
# 配合 crontab 使用:
|
# 配合 crontab 使用:
|
||||||
# 0 3 * * * /path/to/backup.sh >> /var/log/miaosi-backup.log 2>&1
|
# 0 3 * * * /path/to/backup.sh >> /var/log/miaosi-backup.log 2>&1
|
||||||
@ -15,9 +15,9 @@ POSTGRES_USER="${POSTGRES_USER:-postgres}"
|
|||||||
POSTGRES_DB="${POSTGRES_DB:-miaosi}"
|
POSTGRES_DB="${POSTGRES_DB:-miaosi}"
|
||||||
RETAIN_DAYS="${RETAIN_DAYS:-7}"
|
RETAIN_DAYS="${RETAIN_DAYS:-7}"
|
||||||
|
|
||||||
# COS 备份桶(需要先安装 coscli 并配置好凭证)
|
# TOS 备份桶(需要先安装 tosutil 并配置好凭证)
|
||||||
COS_BACKUP_BUCKET="${COS_BACKUP_BUCKET:-}"
|
TOS_BACKUP_BUCKET="${TOS_BACKUP_BUCKET:-}"
|
||||||
COS_BACKUP_PREFIX="${COS_BACKUP_PREFIX:-backups/postgres}"
|
TOS_BACKUP_PREFIX="${TOS_BACKUP_PREFIX:-backups/postgres}"
|
||||||
|
|
||||||
# ---- 执行 ----
|
# ---- 执行 ----
|
||||||
DATE=$(date +%Y%m%d_%H%M%S)
|
DATE=$(date +%Y%m%d_%H%M%S)
|
||||||
@ -32,13 +32,13 @@ docker exec "$POSTGRES_CONTAINER" pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" | g
|
|||||||
|
|
||||||
echo "[$(date)] 本地备份完成: ${BACKUP_DIR}/${FILENAME}"
|
echo "[$(date)] 本地备份完成: ${BACKUP_DIR}/${FILENAME}"
|
||||||
|
|
||||||
# 2. 上传到 COS(如果配置了备份桶)
|
# 2. 上传到 TOS(如果配置了备份桶)
|
||||||
if [ -n "$COS_BACKUP_BUCKET" ]; then
|
if [ -n "$TOS_BACKUP_BUCKET" ]; then
|
||||||
if command -v coscli &> /dev/null; then
|
if command -v tosutil &> /dev/null; then
|
||||||
coscli cp "${BACKUP_DIR}/${FILENAME}" "cos://${COS_BACKUP_BUCKET}/${COS_BACKUP_PREFIX}/${FILENAME}"
|
tosutil cp "${BACKUP_DIR}/${FILENAME}" "tos://${TOS_BACKUP_BUCKET}/${TOS_BACKUP_PREFIX}/${FILENAME}"
|
||||||
echo "[$(date)] 已上传到 COS: ${COS_BACKUP_BUCKET}/${COS_BACKUP_PREFIX}/${FILENAME}"
|
echo "[$(date)] 已上传到 TOS: ${TOS_BACKUP_BUCKET}/${TOS_BACKUP_PREFIX}/${FILENAME}"
|
||||||
else
|
else
|
||||||
echo "[$(date)] 警告: coscli 未安装,跳过 COS 上传"
|
echo "[$(date)] 警告: tosutil 未安装,跳过 TOS 上传"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@ -57,19 +57,19 @@ export function useOSSUpload(fileType: string = 'general'): UseOSSUploadReturn {
|
|||||||
setProgress(10)
|
setProgress(10)
|
||||||
const policy = await api.getUploadPolicy(fileType)
|
const policy = await api.getUploadPolicy(fileType)
|
||||||
|
|
||||||
// 2. 构建 COS 直传 FormData
|
// 2. 构建 TOS 直传 FormData
|
||||||
const fileKey = `${policy.dir}${Date.now()}_${file.name}`
|
const fileKey = `${policy.dir}${Date.now()}_${file.name}`
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('key', fileKey)
|
formData.append('key', fileKey)
|
||||||
formData.append('q-sign-algorithm', policy.q_sign_algorithm)
|
formData.append('x-tos-algorithm', policy.x_tos_algorithm)
|
||||||
formData.append('q-ak', policy.q_ak)
|
formData.append('x-tos-credential', policy.x_tos_credential)
|
||||||
formData.append('q-key-time', policy.q_key_time)
|
formData.append('x-tos-date', policy.x_tos_date)
|
||||||
formData.append('q-signature', policy.q_signature)
|
formData.append('x-tos-signature', policy.x_tos_signature)
|
||||||
formData.append('policy', policy.policy)
|
formData.append('policy', policy.policy)
|
||||||
formData.append('success_action_status', '200')
|
formData.append('success_action_status', '200')
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
|
|
||||||
// 3. 上传到 COS
|
// 3. 上传到 TOS
|
||||||
setProgress(30)
|
setProgress(30)
|
||||||
const xhr = new XMLHttpRequest()
|
const xhr = new XMLHttpRequest()
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
|||||||
@ -136,10 +136,10 @@ export interface RefreshTokenResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadPolicyResponse {
|
export interface UploadPolicyResponse {
|
||||||
q_sign_algorithm: string
|
x_tos_algorithm: string
|
||||||
q_ak: string
|
x_tos_credential: string
|
||||||
q_key_time: string
|
x_tos_date: string
|
||||||
q_signature: string
|
x_tos_signature: string
|
||||||
policy: string
|
policy: string
|
||||||
host: string
|
host: string
|
||||||
dir: string
|
dir: string
|
||||||
@ -426,7 +426,7 @@ class ApiClient {
|
|||||||
// ==================== 文件上传 ====================
|
// ==================== 文件上传 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 COS 上传凭证
|
* 获取 TOS 上传凭证
|
||||||
*/
|
*/
|
||||||
async getUploadPolicy(fileType: string = 'general'): Promise<UploadPolicyResponse> {
|
async getUploadPolicy(fileType: string = 'general'): Promise<UploadPolicyResponse> {
|
||||||
const response = await this.client.post<UploadPolicyResponse>('/upload/policy', {
|
const response = await this.client.post<UploadPolicyResponse>('/upload/policy', {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user