@fleet-frontend/vite-plugin-upload-s3
v0.1.0
Published
A universal plugin for uploading assets to AWS S3 (supports Vite and Webpack)
Readme
@fleet-frontend/vite-plugin-upload-s3
一个通用的插件,用于自动将构建产物上传到 AWS S3 和兼容 S3 的服务。同时支持 Vite 和 Webpack。
特性
- 🚀 自动上传: 构建完成后自动将构建产物上传到 S3
- 🔧 多构建工具支持: 同时支持 Vite 和 Webpack
- ⚡ S3 兼容: 支持任何 S3 兼容的提供商(AWS、DigitalOcean Spaces 等)
- 🎯 智能过滤: 通过文件扩展名、自定义函数或模式过滤文件
- 📦 灵活配置: 支持 ES 模块和 CommonJS
- 🔧 可定制: 灵活的配置选项,适用于不同的使用场景
- 📊 实时进度: 实时显示上传进度,包括文件数量和百分比
- 🔄 重试机制: 失败上传自动重试,支持指数退避
- 🔒 TypeScript: 完整的 TypeScript 支持,包含类型定义
- 🌐 多格式: 支持 ESM 和 CommonJS 模块格式
- 📦 压缩支持: 自动处理 .gz、.br 和其他压缩文件
安装
pnpm add -D @fleet-frontend/vite-plugin-upload-s3
# 或
npm install -D @fleet-frontend/vite-plugin-upload-s3
# 或
yarn add -D @fleet-frontend/vite-plugin-upload-s3注意: 此插件需要 vite (^4.0.0 || ^5.0.0) 或 webpack (^5.0.0) 作为 peer dependency。请安装你正在使用的构建工具:
# Vite 项目
pnpm add -D vite
# Webpack 项目
pnpm add -D webpack快速开始
Vite
// vite.config.ts
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default {
plugins: [
ViteS3(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1'
},
uploadOptions: {
Bucket: 'your-bucket-name'
}
})
]
}Webpack
// webpack.config.js
const { WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')
module.exports = {
plugins: [
new WebpackS3Plugin(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
region: 'us-east-1'
},
uploadOptions: {
Bucket: 'your-bucket-name'
}
})
]
}基本使用
Vite
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
// 或使用统一入口
// import { UploadS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
// 方式 1: 使用 ViteS3(推荐用于 Vite)
ViteS3(!!process.env.UPLOAD_ENABLED, {
basePath: '/build',
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1'
},
uploadOptions: {
Bucket: 'your-bucket-name'
}
})
// 方式 2: 使用统一入口
// UploadS3(!!process.env.UPLOAD_ENABLED, { ... }, 'vite')
]
})Webpack
// webpack.config.js
const { UploadS3, WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')
module.exports = {
// ... 其他 webpack 配置
plugins: [
// 方式 1: 使用统一入口(推荐)
UploadS3(!!process.env.UPLOAD_ENABLED, {
basePath: '/build',
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
region: 'us-east-1'
},
uploadOptions: {
Bucket: 'your-bucket-name'
}
}, 'webpack'),
// 方式 2: 直接使用 WebpackS3Plugin
// new WebpackS3Plugin(!!process.env.UPLOAD_ENABLED, { ... })
]
}注意: Webpack 插件仅在生产模式下上传。开发构建会自动跳过。
配置
必需选项
| 选项 | 类型 | 描述 |
|--------|------|-------------|
| clientConfig | S3ClientConfig | S3Client 类构造函数的配置接口 |
| uploadOptions | PutObjectRequest | PutObjectRequest 选项(除了 Body 和 Key) |
可选选项
| 选项 | 类型 | 默认值 | 描述 |
|--------|------|---------|-------------|
| basePath | string | null | 上传文件在 S3 上的命名空间 |
| directory | string | build.outDir | 要上传的目录 |
| include | string\|RegExp\|Function\|Array | null | 用于匹配包含内容的模式 |
| exclude | string\|RegExp\|Function\|Array | null | 用于匹配排除内容的模式(支持文件扩展名如 .map、.txt 和 glob 模式如 **/*.map) |
| sequentialUploads | boolean | false | 如果为 true,文件将按顺序上传 |
| retryAttempts | number | 3 | 失败上传的重试次数 |
| retryDelay | number | 1000 | 重试尝试之间的延迟(毫秒) |
| forceOverwrite | string\|RegExp\|Function\|Array | null | 应强制覆盖的文件,无需检查它们是否已存在(支持文件扩展名、glob 模式和自定义函数) |
| skipDirectoryPrefix | boolean | false | 如果为 true,跳过目录名作为 S3 路径前缀(直接上传目录内容) |
| failOnError | boolean | true | 如果为 true,当文件上传失败时抛出错误并中断构建;如果为 false,上传失败时只记录警告,不中断构建 |
高级使用
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
ViteS3(!!process.env.UPLOAD_ENABLED, {
basePath: '/cdn/assets',
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-west-2'
},
uploadOptions: {
Bucket: 'your-bucket-name',
CacheControl: 'public, max-age=31536000',
ContentEncoding: 'gzip'
},
include: [
/.*\.(css|js)$/,
function(path) {
return path.includes('assets/') && !path.includes('test')
}
],
exclude: ["**/*.map", "**/*.txt", "**/*.log"], // 排除所有 .map, .txt, .log 文件
forceOverwrite: ["index.html", "**/*.html"], // 强制覆盖所有 HTML 文件
sequentialUploads: false,
})
]
})DigitalOcean Spaces 示例
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
ViteS3(!!process.env.UPLOAD_ENABLED, {
clientConfig: {
credentials: {
accessKeyId: process.env.DO_ACCESS_KEY_ID!,
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY!,
},
endpoint: 'https://fra1.digitaloceanspaces.com',
region: 'fra1'
},
uploadOptions: {
Bucket: 'your-bucket-name'
}
})
]
})环境变量
将你的 AWS 凭证设置为环境变量:
export AWS_ACCESS_KEY_ID="your-access-key-id"
export AWS_SECRET_ACCESS_KEY="your-secret-access-key"
export UPLOAD_ENABLED="true"如果在 EC2/ECS/Lambda 上运行,也可以使用 AWS IAM 角色。
统一入口函数
对于可能在不同构建工具之间切换的项目,你可以使用统一的 UploadS3 函数:
import { UploadS3 } from '@fleet-frontend/vite-plugin-upload-s3'
// 用于 Vite
UploadS3(true, options, 'vite')
// 用于 Webpack
UploadS3(true, options, 'webpack')模块支持
此插件同时支持 ES 模块和 CommonJS:
ES 模块 (ESM)
import { ViteS3, UploadS3, WebpackS3Plugin } from '@fleet-frontend/vite-plugin-upload-s3'CommonJS
const { ViteS3, UploadS3, WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')目录路径控制
插件支持控制是否在 S3 路径中包含目录名作为前缀。当启用 skipDirectoryPrefix 时,只会上传指定目录内的文件和子目录,而不会在 S3 路径中包含目录名本身。
目录路径控制配置
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
ViteS3(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1',
},
uploadOptions: {
Bucket: 'your-bucket-name',
},
// 指定要上传的目录
directory: 'dist',
// 跳过目录名作为 S3 路径前缀
skipDirectoryPrefix: true
})
]
})目录路径控制示例
不使用 skipDirectoryPrefix(默认行为)
本地结构:
dist/
├── index.html
├── assets/
│ ├── style.css
│ └── app.js
└── images/
└── logo.png
S3 结构:
your-bucket/
├── dist/index.html
├── dist/assets/style.css
├── dist/assets/app.js
└── dist/images/logo.png使用 skipDirectoryPrefix: true
本地结构:
dist/
├── index.html
├── assets/
│ ├── style.css
│ └── app.js
└── images/
└── logo.png
S3 结构:
your-bucket/
├── index.html
├── assets/style.css
├── assets/app.js
└── images/logo.png使用场景
- 静态网站托管: 当你想要直接从 S3 存储桶的根目录提供文件时
- CDN 分发: 当你的 CDN 期望文件位于根级别时
- 简洁 URL: 当你想要更简洁、更短的 URL,不包含构建目录名时
- 自定义路径结构: 当你想要使用
basePath来控制 S3 路径结构而不是目录名时
强制覆盖
插件支持强制覆盖特定文件,而无需检查它们是否已存在于 S3 上。这对于应该始终更新的文件(如 index.html 或其他关键文件)很有用。
强制覆盖配置
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
ViteS3(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1',
},
uploadOptions: {
Bucket: 'your-bucket-name',
},
// 强制覆盖特定文件
forceOverwrite: [
"index.html", // 强制覆盖 index.html
"**/*.html", // 强制覆盖所有 HTML 文件
/\.html$/, // 强制覆盖以 .html 结尾的文件
(path) => path.includes('critical') // 强制覆盖包含 'critical' 的文件
]
})
]
})强制覆盖模式类型
forceOverwrite 选项支持与 include 和 exclude 相同的模式类型:
文件扩展名过滤
forceOverwrite: [".html", ".css", ".js"] // 强制覆盖特定文件扩展名Glob 模式过滤
forceOverwrite: [
"index.html", // 强制覆盖 index.html
"**/*.html", // 强制覆盖所有 HTML 文件
"dist/assets/*.js", // 强制覆盖 dist/assets/ 中的 JS 文件
"**/critical/**" // 强制覆盖 critical 目录中的文件
]正则表达式过滤
forceOverwrite: [/\.html$/, /index\./, /critical/] // 使用正则表达式函数过滤
forceOverwrite: [
(path) => path.includes('index'),
(path) => path.endsWith('.html'),
(path) => path.includes('critical')
]混合模式过滤
forceOverwrite: [
"index.html", // 文件名
"**/*.html", // glob 模式
/\.critical\./, // 正则表达式
(path) => path.includes('temp') // 函数
]文件过滤
插件支持使用多种模式类型进行灵活的文件过滤:
文件扩展名过滤
exclude: [".map", ".txt", ".log"] // 排除特定扩展名的文件Glob 模式过滤
exclude: [
"**/*.map", // 排除所有 .map 文件
"**/test/**", // 排除 test 目录下的所有文件
"**/*.test.*", // 排除测试文件
"dist/assets/*.js" // 排除特定路径下的 .js 文件
]正则表达式过滤
exclude: [/\.map$/, /\.txt$/, /test/] // 使用正则表达式函数过滤
exclude: [
(path) => path.includes('test'),
(path) => path.endsWith('.map')
]混合模式过滤
exclude: [
".map", // 文件扩展名
"**/*.txt", // glob 模式
/\.log$/, // 正则表达式
(path) => path.includes('temp') // 函数
]错误处理配置
插件支持配置上传失败时的行为。默认情况下,当文件上传失败时,插件会抛出错误并中断构建流程。你可以通过 failOnError 选项来控制这个行为。
默认行为(failOnError: true)
当 failOnError 为 true(默认值)时,如果文件上传失败,构建流程会被中断:
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
ViteS3(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1',
},
uploadOptions: {
Bucket: 'your-bucket-name',
},
// failOnError 默认为 true,上传失败会中断构建
// failOnError: true
})
]
})注意:文件已存在(跳过上传)的情况不算失败,不会中断构建。只有真正需要上传但上传失败的文件才会触发错误。
允许上传失败继续构建(failOnError: false)
如果你希望即使上传失败也继续构建流程,可以设置 failOnError: false:
// vite.config.ts
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
export default defineConfig({
plugins: [
ViteS3(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1',
},
uploadOptions: {
Bucket: 'your-bucket-name',
},
// 上传失败时不中断构建,只记录警告
failOnError: false
})
]
})使用场景
- failOnError: true(默认):适用于生产环境,确保所有文件都成功上传到 CDN
- failOnError: false:适用于开发环境或测试场景,允许构建继续完成,即使上传失败
插件顺序和压缩支持
Vite
插件现在按照你在 Vite 配置中定义的顺序执行,使其能够与压缩插件正常工作:
import { defineConfig } from 'vite'
import { ViteS3 } from '@fleet-frontend/vite-plugin-upload-s3'
import { compression } from 'vite-plugin-compression'
export default defineConfig({
plugins: [
// 其他插件...
// 压缩插件 - 生成 .gz 文件
compression({
algorithm: 'gzip',
ext: '.gz'
}),
// S3 上传插件 - 在压缩插件之后执行
ViteS3(true, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'us-east-1',
},
uploadOptions: {
Bucket: 'your-bucket-name',
},
// 包含压缩文件
include: [/\.(js|css|html|gz|br)$/],
// 排除源映射文件
exclude: ["**/*.map"]
})
]
})Webpack
对于 Webpack,插件在构建完成后自动运行。你可以将其与其他 Webpack 插件一起使用:
const { WebpackS3Plugin } = require('@fleet-frontend/vite-plugin-upload-s3')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
plugins: [
// 压缩插件
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
}),
// S3 上传插件 - 在构建完成后运行
new WebpackS3Plugin(!!process.env.UPLOAD_ENABLED, {
clientConfig: {
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
region: 'us-east-1',
},
uploadOptions: {
Bucket: 'your-bucket-name',
},
// 包含压缩文件
include: [/\.(js|css|html|gz|br)$/],
// 排除源映射文件
exclude: ['**/*.map']
})
]
}进度显示
插件在上传过程中提供实时进度更新:
🚀 Uploading 15 files to S3...
[8/15] 53% ✅ 6 ❌ 2 | 📁 style.css进度信息:
[8/15]- 当前文件编号 / 总文件数53%- 完成百分比✅ 6- 成功上传的文件数❌ 2- 失败的上传数📁 style.css- 正在处理的当前文件
构建工具支持
此插件使用相同的配置选项同时支持 Vite 和 Webpack。主要区别在于如何导入和使用插件:
| 特性 | Vite | Webpack |
|---------|------|---------|
| 导入 | import { ViteS3 } from '...' | const { WebpackS3Plugin } = require('...') |
| 使用 | ViteS3(enabled, options) | new WebpackS3Plugin(enabled, options) |
| 统一入口 | UploadS3(enabled, options, 'vite') | UploadS3(enabled, options, 'webpack') |
| 构建钩子 | closeBundle | done |
| 开发模式 | 由 enabled 参数控制 | 自动跳过 |
| 输出检测 | 从 config.build.outDir | 从 compiler.options.output.path |
Vite 支持
插件完全支持 Vite 的所有功能:
- 使用 Vite 的
closeBundle钩子确保可靠的上传时机 - 自动从 Vite 配置中检测构建输出目录
- 与其他 Vite 插件无缝协作
- 尊重
enabled参数进行条件上传
Webpack 支持
插件支持 Webpack 5:
- 使用 Webpack 的
done钩子在构建完成后上传 - 在开发模式下自动跳过上传(无论
enabled参数如何) - 从 Webpack 配置中读取输出目录
- 支持所有 Webpack 5 功能
重要: 使用 Webpack 时,请确保你使用的是 Webpack 5 或更高版本。不支持 Webpack 4。
示例
查看 examples/ 目录以获取更详细的使用示例:
有关 Webpack 示例,请参阅上面的 Webpack 部分中的使用模式。
开发
# 安装依赖
pnpm install
# 构建插件
pnpm build
# 监听模式
pnpm dev
# 代码检查
pnpm lint
# 类型检查
pnpm type-check许可证
MIT
贡献
欢迎贡献!请随时提交 Pull Request。
