Skip to content

uploadToCOS

上传文件夹到腾讯云 COS(对象存储)。

前置依赖

依赖参数

参数名类型说明
deps.COSCOSConstructorCOS SDK 构造函数
deps.existsSyncFileSystem['existsSync']检查文件是否存在
deps.statSyncFileSystem['statSync']获取文件状态
deps.readdirSyncFileSystem['readdirSync']读取目录内容
deps.readFileSyncFileSystem['readFileSync']读取文件内容
deps.readFileFileSystem['readFile']异步读取文件(可选,提供后可提高并发性能)
deps.joinPath['join']路径拼接
deps.basenamePath['basename']获取路径基础名
deps.relativePath['relative']获取相对路径
deps.normalizePath['normalize']标准化路径
deps.logConsole['log']日志输出函数
deps.errorConsole['error']错误日志函数
deps.getSecret(type: SecretType) => TencentCloudSecret | null获取密钥函数

环境要求

  • cos-nodejs-sdk-v5: 腾讯云 COS Node.js SDK
bash
npm install cos-nodejs-sdk-v5

函数签名

typescript
function uploadToCOS(
  options: UploadToCOSOptions,
  deps: UploadToCOSDeps
): Promise<UploadToCOSResult>

interface UploadToCOSOptions {
  /** 本地文件夹路径 */
  localPath: string
  /** COS 存储桶名称 */
  bucket: string
  /** COS 地域 */
  region: string
  /** 云端路径前缀 */
  cloudPath?: string
  /** 密钥类型 */
  secretType: SecretType
  /** 是否包含根文件夹 */
  includeRootFolder?: boolean
  /** 是否显示进度 */
  showProgress?: boolean
  /** 并发上传数量,默认 10 */
  concurrency?: number
}

interface UploadToCOSResult {
  success: boolean
  message?: string
  error?: string
  filesCount?: number
  failedFiles?: string[]
}

参数

参数名类型必填说明
options.localPathstring本地文件夹路径
options.bucketstringCOS 存储桶名称
options.regionstringCOS 地域(如 ap-guangzhou
options.cloudPathstring云端路径前缀,默认为空
options.secretTypeSecretType密钥类型(personal/individual/enterprise
options.includeRootFolderboolean是否包含根文件夹名称,默认 false
options.showProgressboolean是否显示上传进度,默认 true
options.concurrencynumber并发上传数量,默认 10

返回值

类型说明
Promise<UploadToCOSResult>上传结果对象

UploadToCOSResult 字段:

字段类型说明
successboolean是否成功
messagestring?成功消息
errorstring?错误消息
filesCountnumber?成功上传的文件数量
failedFilesstring[]?失败的文件列表

工作原理

  1. 验证本地路径是否存在且为文件夹
  2. 通过 getSecret 获取腾讯云密钥
  3. 初始化 COS SDK
  4. 递归遍历文件夹获取所有文件
  5. 根据 includeRootFolder 选项计算云端路径
  6. 并发上传文件到 COS(默认并发数为 10)
  7. 返回上传结果和失败文件列表

includeRootFolder 选项说明:

  • false(默认):不包含根文件夹名称,直接上传文件夹内容

    • 本地:./dist/index.html
    • COS:/web/index.html
  • true:保留根文件夹名称作为路径的一部分

    • 本地:./dist/index.html
    • COS:/web/dist/index.html

示例

基本用法

typescript
import { uploadToCOS } from 'zcw-shared/functions/tencent-cloud/upload.cos'
import type { SecretType, TencentCloudSecret } from 'zcw-shared/types/tencent-cloud'
import COS from 'cos-nodejs-sdk-v5'
import fs from 'fs'
import path from 'path'

// 自定义密钥获取函数
// 注意:实际使用时应该从环境变量或安全的密钥管理服务获取
function getSecret(type: SecretType): TencentCloudSecret | null {
  // 根据类型返回对应的密钥
  // 这里应该从环境变量或密钥管理服务获取,而不是硬编码
  if (type === 'personal') {
    return {
      secretId: process.env.TENCENT_SECRET_ID || '',
      secretKey: process.env.TENCENT_SECRET_KEY || ''
    }
  }
  return null
}

const result = await uploadToCOS(
  {
    localPath: './dist',
    bucket: 'my-bucket-1234567890',
    region: 'ap-guangzhou',
    cloudPath: '/web',
    secretType: 'personal',
    includeRootFolder: false,
    showProgress: true
  },
  {
    COS,
    existsSync: fs.existsSync,
    statSync: fs.statSync,
    readdirSync: fs.readdirSync,
    readFileSync: fs.readFileSync,
    readFile: fs.readFile,  // 可选,提供后可提高并发性能
    join: path.join,
    basename: path.basename,
    relative: path.relative,
    normalize: path.normalize,
    log: console.log,
    error: console.error,
    getSecret
  }
)

if (result.success) {
  console.log(`上传成功,共 ${result.filesCount} 个文件`)
} else {
  console.error(`上传失败: ${result.error}`)
}

部署前端项目

typescript
async function deployFrontend() {
  const result = await uploadToCOS(
    {
      localPath: './dist',
      bucket: 'my-website-1234567890',
      region: 'ap-guangzhou',
      cloudPath: '/',
      secretType: 'personal',
      includeRootFolder: false,  // 直接上传 dist 内容到根目录
      showProgress: true
    },
    {
      COS,
      existsSync: fs.existsSync,
      statSync: fs.statSync,
      readdirSync: fs.readdirSync,
      readFileSync: fs.readFileSync,
      readFile: fs.readFile,  // 可选,提供后可提高并发性能
      join: path.join,
      basename: path.basename,
      relative: path.relative,
      normalize: path.normalize,
      log: console.log,
      error: console.error,
      getSecret: (type) => {
        // 自定义密钥获取逻辑
        return {
          secretId: process.env.TENCENT_SECRET_ID || '',
          secretKey: process.env.TENCENT_SECRET_KEY || ''
        }
      }
    }
  )

  return result
}

常见错误

本地路径不存在

typescript
{ success: false, error: '本地路径不存在: ./dist' }

路径不是文件夹

typescript
{ success: false, error: '路径不是文件夹: ./file.txt' }

无效的密钥类型

typescript
{ success: false, error: '无效的密钥类型: invalid' }

文件夹为空

typescript
{ success: false, error: '文件夹为空,没有文件可上传' }

注意事项

  • 如果 COS 中已存在同名文件,会被覆盖
  • 不会上传空文件夹(COS 不支持空文件夹)
  • 路径分隔符会自动标准化为 /
  • 确保密钥有上传文件到指定存储桶的权限
  • 对于大量文件,可以通过增加 concurrency 参数加快上传速度
  • 并发数设置建议:10-50,过大可能导致服务器拒绝连接或网络拥塞

优化上传速度

typescript
// 方法1:增加并发数 + 使用异步读取
const result = await uploadToCOS(
  {
    localPath: './dist',
    bucket: 'my-bucket-1234567890',
    region: 'ap-guangzhou',
    cloudPath: '/',
    secretType: 'personal',
    concurrency: 30,  // 设置并发数为 30
    showProgress: true
  },
  {
    COS,
    existsSync: fs.existsSync,
    statSync: fs.statSync,
    readdirSync: fs.readdirSync,
    readFileSync: fs.readFileSync,
    readFile: fs.readFile,  // 提供异步读取以进一步提高性能
    // ... 其他依赖
  }
)

性能优化要点:

  • 提供 readFile 异步读取函数可显著提高并发性能
  • 并发数建议根据网络环境和服务器承受能力调整(10-50)
  • 对于大量小文件,可以适当提高并发数