@dd-code/oss-uploader
v0.1.14
Published
Upload local directories to OSS (currently Huawei OBS) with pluggable middleware and providers.
Maintainers
Readme
OSS Uploader
将本地目录上传到对象存储(当前支持华为云 OBS)的 npm 包,提供 CLI 与库两种使用方式。采用中间件 + Provider 抽象,便于后续切换或接入其他云厂商(如阿里云 OSS、AWS S3)。
功能特性
- 目录上传:按本地目录结构上传到指定 Bucket,远端 key 规则为
basePrefix / env / pathPrefix / 相对路径。 - 同名跳过:上传前按前缀调用 OBS
listObjects拉取已有 key,内存 Set 判断是否存在,仅 1 次(或少量)列表请求,不再逐文件 head,大幅提速。 - 强制覆盖:环境变量
FILE_RE_WHITE_LIST(JSON 数组)中匹配到的 key 始终上传、不跳过。 - 过滤规则:支持
--include/--exclude通配符(如**/*.js、**/*.map),使用 micromatch。 - 上传进度:控制台显示进度条(当前/总数、百分比),基于 cli-progress。
- CDN 地址:可选配置
OBS_CDN_BASE_URL,上传完成后控制台打印每个文件的访问 URL。 - 可切换 Provider:通过环境变量
OSS_PROVIDER与单一配置文件ProviderConfigResolverImpl即可切换云厂商,无需改业务代码。
环境要求
- Node.js:>= 18
- 包管理:npm / pnpm / yarn 均可
安装
# 从本地或私有 registry 安装(请将 @your-scope/oss-uploader 替换为实际包名与版本)
npm install @your-scope/oss-uploader
# 或在本仓库根目录
pnpm install
pnpm run build配置
所有配置通过环境变量读取,不依赖 .env 文件(若使用 dotenv 可在进程内自行加载)。
华为 OBS(当前默认)
| 变量名 | 必填 | 说明 |
|--------|------|------|
| OBS_ENDPOINT | 是 | OBS 服务地址,如 https://obs.cn-north-4.myhuaweicloud.com |
| OBS_ACCESS_KEY | 是 | 访问密钥 AK |
| OBS_SECRET_KEY | 是 | 访问密钥 SK |
| OBS_REGION | 否 | 区域,如 cn-north-4 |
| OBS_BASE_PREFIX | 否 | 所有对象 key 的基础前缀(仅在配置中,不通过 CLI 暴露),如 company/project-a |
| OBS_CDN_BASE_URL | 否 | CDN 加速根地址,用于上传完成后拼出访问 URL,如 https://cdn.example.com |
| FILE_RE_WHITE_LIST | 否 | JSON 数组字符串,key 包含其中任一项则强制上传不跳过,如 ["index.html","sw.js"] |
多云与切换
| 变量名 | 必填 | 说明 |
|--------|------|------|
| OSS_PROVIDER | 否 | 当前使用的存储,默认 huawei。后续可扩展 aliyun、aws 等。 |
示例 .env.example(可复制为 .env 并按实际填写):
# 华为 OBS
OBS_ENDPOINT=https://obs.cn-north-4.myhuaweicloud.com
OBS_ACCESS_KEY=your-access-key-id
OBS_SECRET_KEY=your-secret-access-key
OBS_REGION=cn-north-4
OBS_BASE_PREFIX=company/project-a
OBS_CDN_BASE_URL=https://cdn.example.com
# 可选:强制覆盖的 key 片段(JSON 数组)
# FILE_RE_WHITE_LIST=["index.html","sw.js"]
# 可选:切换存储厂商
# OSS_PROVIDER=huawei使用方式
一、命令行(CLI)
安装后可通过 oss-uploader 命令上传本地目录(若未全局安装,可使用 npx oss-uploader 或 pnpm exec oss-uploader)。
# 基本用法:上传 ./dist 到指定 bucket
oss-uploader ./dist -b my-bucket --path-prefix app/v1/
# 带 include/exclude
oss-uploader ./dist -b my-bucket --path-prefix app/v1/ \
--include "**/*.js" --include "**/*.css" \
--exclude "**/*.map"
# 指定环境标识(会参与 key 拼接)
npx oss-uploader ./dist -e dev --path-prefix web/appCLI 参数说明
| 参数 | 简写 | 必填 | 说明 |
|------|------|------|------|
| localDir | - | 是 | 本地目录路径 |
| --bucket | -b | 否 | Bucket 名称,默认 hr-uat |
| --env | -e | 否 | 环境标识,参与 key 拼接,默认 dev |
| --path-prefix | - | 否 | 业务路径前缀,拼在 base/env 之后 |
| --include | - | 否 | 包含的文件模式,可多次 |
| --exclude | - | 否 | 排除的文件模式,可多次 |
上传过程中会显示进度条(当前/总数、百分比),结束后输出:成功/失败/跳过的 key 列表;若配置了 CDN 会打印访问地址。
二、作为库使用
包入口仅暴露一个上传方法 upload 及类型 UploadOptions、UploadResult,内部配置与 Reporter 固定(从环境变量读取,进度输出到控制台)。
import { upload, type UploadOptions, type UploadResult } from '@dd-code/oss-uploader';
const result: UploadResult = await upload({
localDir: './dist',
bucket: 'my-bucket',
env: 'prod',
pathPrefix: 'app/v1',
include: ['**/*.js', '**/*.css'],
exclude: ['**/*.map'],
});
console.log(`总数: ${result.total}, 成功: ${result.success.length}, 失败: ${result.failed.length}, 跳过: ${result.skipped.length}`);需要自定义 Reporter(如对接企业微信、钉钉)时,可直接使用中间件层 uploadDirectory 并传入 reporter(见源码 middleware/OssService.ts)。
远端 Key 规则
单个文件的远端 key 由以下部分按顺序拼接(空段不参与):
basePrefix / env / pathPrefix / 相对路径- basePrefix:来自配置
OBS_BASE_PREFIX,仅在配置中设置,不通过 CLI 暴露,默认都会带上前缀。 - env:CLI 的
--env或库入参的env。 - pathPrefix:CLI 的
--path-prefix或库入参的pathPrefix。 - 相对路径:相对
localDir的路径,保持目录结构。
示例:basePrefix=company/proj、env=prod、pathPrefix=web、本地文件 dist/js/app.js → 远端 key:company/proj/prod/web/js/app.js。
架构与设计
分层结构
┌─────────────────────────────────────────────────────────┐
│ CLI / 业务代码 │
│ CLI 调用 uploadDirectory,库调用 upload(options) │
└───────────────────────────┬─────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────┐
│ 中间件层:OssService │
│ 解析配置、创建 StorageClient、组装 DirectoryUploadFlow │
└───────────────────────────┬─────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────┐
│ 流程层:UploadService / DirectoryUploadFlow │
│ 收集文件 → 过滤 → listObjectKeys 拉取已有 key → │
│ 逐文件 Set 判断 / head 回退 → putObject → Reporter │
└───────────────────────────┬─────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────┐
│ Provider 层:StorageClient 实现(如 HuaweiObsClient) │
│ listObjectKeys、headObject、putObject 对应 OBS API │
└─────────────────────────────────────────────────────────┘设计模式简述
- Facade:对外只暴露
upload(options),内部固定配置与 Reporter。 - Template Method:
UploadService定义「收集 → 过滤 → 上传 → 上报」骨架,DirectoryUploadFlow继承并执行。 - Strategy / Adapter:各云厂商实现
StorageClient(含可选listObjectKeys),由StorageFactory按配置创建。
目录结构(源码)
src/
├── index.ts # 包入口,仅导出 upload / UploadOptions / UploadResult
├── cli.ts # CLI 入口(oss-uploader)
├── config/
│ ├── types.ts # 配置与 Provider 类型
│ ├── EnvConfigResolverImpl.ts
│ ├── ProviderConfigResolverImpl.ts
│ └── loadObsCredentialsFromUrl.ts
├── core/
│ ├── StorageClient.ts # 存储客户端接口(含可选 listObjectKeys)
│ ├── StorageFactory.ts
│ ├── UploadService.ts # 上传流程基类(收集 → list/head 判断 → put)
│ ├── DirectoryUploadFlow.ts
│ ├── reporter.ts # Reporter 与 ConsoleReporter(含进度条)
│ ├── filters.ts
│ └── urlHelper.ts
├── middleware/
│ └── OssService.ts # 统一 API,解析配置并调用 Flow
└── providers/
└── huawei/
└── HuaweiObsClient.ts # OBS 的 listObjectKeys / headObject / putObject扩展与二次开发
切换云厂商(如阿里云 OSS)
- 在
src/config/types.ts中扩展ProviderType与对应配置类型(如AliyunOssConfig)、在ProviderConfig中增加分支。 - 在
src/config/ProviderConfigResolverImpl.ts的resolve()中增加case 'aliyun',从环境变量读取阿里云配置,返回{ providerConfig, basePrefix, cdnBaseUrl }。 - 在
src/core/StorageFactory.ts中增加case 'aliyun',创建阿里云 Provider 实例。 - 在
src/providers/下新增对应客户端,实现StorageClient的headObject、putObject,可选实现listObjectKeys以启用「先 list 再判断」的加速逻辑。
业务侧只需设置 OSS_PROVIDER=aliyun 及对应环境变量,无需改 OssService / CLI。
自定义 Reporter
实现接口(含可选 onProgress):
interface Reporter {
onStart?(context: { env?, bucket, basePrefix, pathPrefix, localDir, cdnBaseUrl? }): void | Promise<void>;
onProgress?(current: number, total: number): void | Promise<void>;
onFileResult?(result: UploadFileResult): void | Promise<void>;
onComplete?(summary: UploadSummary): void | Promise<void>;
}需直接使用中间件层 uploadDirectory 并传入 reporter(包入口 upload() 不暴露 reporter 参数),详见 middleware/OssService.ts。
工具方法
- 包入口仅导出
upload、UploadOptions、UploadResult。buildCdnAccessUrl等工具在内部使用,若需在业务中拼 CDN URL 可参考core/urlHelper.ts。
构建与发布
# 安装依赖
pnpm install
# 构建(Rollup 输出 dist/index.cjs、dist/cli.cjs 等)
pnpm run build
# 监听模式
pnpm run dev发布前请将 package.json 中的 name 改为实际 scope 与包名(当前为 @dd-code/oss-uploader),按需配置 publishConfig 与 registry。
