luoyu-oss-unified
v0.0.8
Published
luoyu-oss-unified 是一个统一的对象存储服务(OSS)客户端库,支持阿里云、腾讯云、七牛云、MinIO等多种云服务商。提供一致的API接口,简化多平台对象存储集成,支持分片上传、断点续传等高级功能,适用于Node.js和Web应用开发。
Maintainers
Readme
luoyu-oss-unified - 统一对象存储客户端
一个统一的多平台对象存储客户端,支持 MinIO、阿里云、腾讯云、七牛云等多种对象存储服务。
特性
- 🚀 统一的 API 接口,支持多种对象存储服务
- 📦 轻量级设计,易于集成
- 🔧 支持上传、下载、删除、列表、目录操作等功能
- 🔄 支持分片上传和断点续传
- 🛡️ TypeScript 支持,类型安全
- ⚡ 事件监听机制,支持进度监控和错误处理
安装
npm install luoyu-oss-unified快速开始
基本使用
import { OSSClient, OSSProvider } from 'luoyu-oss-unified';
// MinIO 配置
const minioConfig = {
provider: OSSProvider.MINIO,
endpoint: 'http://your-minio-server.com',
accessKeyId: 'your-access-key',
accessKeySecret: 'your-secret-key',
bucket: 'your-bucket',
region: 'us-east-1',
};
// 创建客户端
const ossClient = new OSSClient(minioConfig);
// 上传文件
await ossClient.upload({
path: 'images/photo.jpg',
file: fs.readFileSync('./photo.jpg'),
contentType: 'image/jpeg',
});七牛云配置示例
const qiniuConfig = {
provider: OSSProvider.QINIU,
accessKeyId: 'your-access-key',
accessKeySecret: 'your-secret-key',
bucket: 'your-bucket',
region: 'z0', // z0: 华东, z1: 华北, z2: 华南, na0: 北美, as0: 新加坡
domain: 'your-domain.qiniudn.com',
};阿里云配置示例
const aliyunConfig: OSSConfig = {
provider: OSSProvider.ALIYUN,
endpoint: 'https://oss-cn-guangzhou.aliyuncs.com',
accessKeyId: 'your-aliyun-access-key-id',
accessKeySecret: 'your-aliyun-access-key-secret',
bucket: 'your-bucket-name',
region: 'oss-cn-guangzhou',
};腾讯云配置示例
const tencentConfig: OSSConfig = {
provider: OSSProvider.TENCENT,
accessKeyId: 'your-tencent-access-key-id',
accessKeySecret: 'your-tencent-access-key-secret',
bucket: 'your-tencent-bucket-name',
region: 'ap-guangzhou',
domain: 'https://your-bucket-name.cos.ap-guangzhou.myqcloud.com',
};API 文档
OSSClient
构造函数
new OSSClient(config: OSSConfig)参数:
config- 配置对象
配置选项:
provider- 存储提供商 (MINIO,ALIYUN,TENCENT,QINIU)endpoint- 服务端点accessKeyId- 访问密钥 IDaccessKeySecret- 访问密钥bucket- 存储桶名称region- 区域secure- 是否使用 HTTPS (默认: true)domain- 自定义域名 (可选)
获取底层OSS客户端实例
const client = ossClient.getOssClient();返回值:
- 底层OSS客户端实例,可用于直接调用提供商特定的API
示例:
const minioClient = ossClient.getOssClient();
// 现在可以使用 minioClient 直接调用 MinIO 特定的方法上传文件
await ossClient.upload(options: UploadOptions)参数:
path- 文件路径file- 文件内容 (Buffer | string | ReadableStream)contentType- 内容类型 (可选)mode- 上传模式 (普通/分片)partSize- 分片大小 (分片上传时使用)progressCallback- 进度回调函数
示例:
await ossClient.upload({
path: 'files/document.pdf',
file: fs.readFileSync('./document.pdf'),
contentType: 'application/pdf',
progressCallback: (progress) => {
console.log(`上传进度: ${progress.percent}%`);
},
});下载文件
const buffer = await ossClient.download(options: DownloadOptions)参数:
path- 文件路径destination- 本地保存路径 (可选)
示例:
const fileBuffer = await ossClient.download({
path: 'images/photo.jpg',
});删除文件
await ossClient.delete(path: string)示例:
await ossClient.delete('images/photo.jpg');删除目录
await ossClient.deleteDirectory(path: string)示例:
await ossClient.deleteDirectory('documents/');列出文件
const result = await ossClient.list(options?: ListOptions)参数:
prefix- 前缀过滤 (可选)limit- 返回数量限制 (可选)delimiter- 分隔符 (可选)marker- 分页标记 (可选)
示例:
const result = await ossClient.list({
prefix: 'images/',
limit: 10,
});
console.log(result.objects); // 文件列表列出所有文件
const objects = await ossClient.listAll(prefix?: string)参数:
prefix- 文件前缀过滤条件 (可选)
示例:
const allFiles = await ossClient.listAll('images/');
console.log(allFiles); // 所有匹配的文件对象数组创建目录
await ossClient.createDirectory(path: string)示例:
await ossClient.createDirectory('documents/');列出目录
const directories = await ossClient.listDirectories(prefix?: string)示例:
const dirs = await ossClient.listDirectories('images/');
console.log(dirs); // 目录列表获取签名 URL
const url = await ossClient.getSignedUrl(path: string, expires: number)参数:
path- 文件路径expires- 过期时间 (秒,默认: 3600)
示例:
const url = await ossClient.getSignedUrl('images/photo.jpg', 3600);
console.log(url); // 可访问的临时链接获取文件 URL
const url = await ossClient.getUrl(path: string)参数:
path- 文件路径
示例:
const url = await ossClient.getUrl('images/photo.jpg');
console.log(url); // 文件访问 URL复制文件
const destPath = await ossClient.copy(sourcePath: string, destPath: string)参数:
sourcePath- 源文件路径destPath- 目标文件路径
示例:
const result = await ossClient.copy(
'images/photo.jpg',
'images/photo-backup.jpg',
);
console.log(result); // 复制成功后的目标路径移动文件
await ossClient.move(sourcePath: string, destPath: string)参数:
sourcePath- 源文件路径destPath- 目标文件路径
示例:
await ossClient.move('images/photo.jpg', 'images/old/photo.jpg');获取文件大小
const size = await ossClient.getFileSize(path: string)参数:
path- 文件路径
示例:
const size = await ossClient.getFileSize('images/photo.jpg');
console.log(size); // 文件大小(字节)分片上传相关方法
初始化分片上传
const uploadInfo = await ossClient.initiateMultipartUpload(path: string, partSize?: number)参数:
path- 文件路径partSize- 分片大小 (可选)
示例:
const uploadInfo = await ossClient.initiateMultipartUpload('videos/large.mp4');上传单个分片
const partInfo = await ossClient.uploadPart(uploadInfo: MultipartUploadInfo, partNumber: number, data: Buffer, start: number, end: number)示例:
const partInfo = await ossClient.uploadPart(
uploadInfo,
1,
fileBuffer,
0,
1024 * 1024,
);完成分片上传
const result = await ossClient.completeMultipartUpload(uploadInfo: MultipartUploadInfo, parts: PartInfo[])示例:
const result = await ossClient.completeMultipartUpload(uploadInfo, parts);取消分片上传
await ossClient.abortMultipartUpload(uploadInfo: MultipartUploadInfo)示例:
await ossClient.abortMultipartUpload(uploadInfo);列出已上传的分片
const parts = await ossClient.listParts(uploadInfo: MultipartUploadInfo)示例:
const parts = await ossClient.listParts(uploadInfo);断点续传
const result = await ossClient.resumeUpload(options: ResumeUploadOptions)示例:
const result = await ossClient.resumeUpload({
path: 'videos/large.mp4',
file: fs.readFileSync('./large.mp4'),
progressCallback: (progress) => {
console.log(`Upload progress: ${progress.percent}%`);
},
});批量操作
批量上传
const results = await ossClient.batchUpload(files: Array<{ path: string; file: Buffer | string }>)示例:
const results = await ossClient.batchUpload([
{ path: 'images/1.jpg', file: fs.readFileSync('./1.jpg') },
{ path: 'images/2.jpg', file: fs.readFileSync('./2.jpg') },
]);批量删除
await ossClient.batchDelete(paths: string[])示例:
await ossClient.batchDelete(['images/1.jpg', 'images/2.jpg']);高级功能扩展
事件监听器
OSSClient 内置了事件监听功能,可以直接使用 on、off 和 emit 方法来管理事件。
import { OSSClient,UploadOptions } from 'luoyu-oss-unified';
class EnhancedOSSClient extends OSSClient {
async uploadWithEvents(options: UploadOptions) {
this.emit('upload:start', options);
try {
const result = await super.upload(options);
this.emit('upload:complete', result);
return result;
} catch (error) {
this.emit('upload:error', error);
throw error;
}
}
}
// 创建客户端
const ossClient = new EnhancedOSSClient(minioConfig);
// 监听上传开始事件
ossClient.on('upload:start', (options) => {
console.log('上传开始:', options.path);
});
// 监听上传进度事件
ossClient.on('upload:progress', (progress) => {
console.log(`上传进度: ${progress.percent}%`);
});
// 监听上传完成事件
ossClient.on('upload:complete', (result) => {
console.log('上传完成:', result);
});
// 监听上传错误事件
ossClient.on('upload:error', (error) => {
console.error('上传错误:', error);
});
// 执行上传操作
await ossClient.uploadWithEvents({
path: 'files/document.pdf',
file: fs.readFileSync('./document.pdf'),
progressCallback: (progress) => {
ossClient.emit('upload:progress', progress);
console.log(`Upload progress: ${progress.percent}%`);
},
});
// 之后可以取消监听
ossClient.off('upload:progress');分片上传
对于大文件,推荐使用分片上传:
await ossClient.upload({
path: 'videos/large-video.mp4',
file: fs.readFileSync('./large-video.mp4'),
mode: UploadMode.MULTIPART,
partSize: 10 * 1024 * 1024, // 10MB 每片
progressCallback: (progress) => {
console.log(`Upload progress: ${progress.percent}%`);
},
});存在性检查
const exists = await ossClient.exists('images/photo.jpg');
console.log(exists); // true/false获取文件信息
const fileInfo = await ossClient.head('images/photo.jpg');
if (fileInfo) {
console.log(fileInfo.size); // 文件大小
console.log(fileInfo.lastModified); // 最后修改时间
}支持的提供商
- ✅ MinIO - 高性能对象存储
- ✅ 七牛云 - 国内主流 CDN 服务商
- ✅ 阿里云 OSS - 企业级对象存储服务
- ✅ 腾讯云 COS - 腾讯云对象存储
环境要求
- Node.js >= 14.0.0
- TypeScript >= 4.0.0 (可选,但推荐)
完整测试代码示例
import {
OSSClient,
OSSConfig,
OSSProvider,
UploadMode,
} from 'luoyu-oss-unified';
import fs from 'fs';
import path from 'path';
// MinIO 配置
const minioConfig = {
provider: OSSProvider.MINIO,
endpoint: 'http://your-minio-server.com',
accessKeyId: 'your-access-key',
accessKeySecret: 'your-secret-key',
bucket: 'your-bucket',
region: 'us-east-1',
};
// 七牛云配置
const qiniuConfig: OSSConfig = {
provider: OSSProvider.QINIU,
accessKeyId: 'your-access-key',
accessKeySecret: 'your-secret-key',
bucket: 'your-bucket',
region: 'z0',
domain: 'your-domain.qiniudn.com',
};
// 创建客户端
const ossClient = new OSSClient(minioConfig);
// 上传文件
export async function uploadFile() {
const filePath = path.join(__dirname, '../upload/file.jpg');
if (!fs.existsSync(filePath)) {
throw new Error(`File does not exist: ${filePath}`);
}
const result = await ossClient.upload({
path: 'images/test.jpg',
file: fs.readFileSync(filePath),
contentType: 'image/jpeg',
progressCallback: (progress) => {
console.log(`Upload progress: ${progress.percent}%`);
},
});
console.log('File uploaded:', result);
}
// 下载文件
export async function downloadFile() {
const buffer = await ossClient.download({
path: 'images/test.jpg',
});
return buffer;
}
// 列出文件
export async function listFiles() {
const result = await ossClient.list({
prefix: 'images/',
limit: 10,
});
console.log('Files:', result.objects);
}
// 列出目录
export async function listDirectories() {
const dirs = await ossClient.listDirectories('images/');
console.log('Directories:', dirs);
return dirs;
}
// 创建目录
export async function createDir() {
await ossClient.createDirectory('documents/');
}
// 上传大文件
export async function uploadLargeFile() {
const filePath = path.join(__dirname, '../upload/large-file.mp4');
if (!fs.existsSync(filePath)) {
throw new Error(`File does not exist: ${filePath}`);
}
const result = await ossClient.upload({
path: 'videos/large.mp4',
file: fs.readFileSync(filePath),
mode: UploadMode.MULTIPART,
partSize: 10 * 1024 * 1024,
progressCallback: (progress) => {
console.log(`Upload progress: ${progress.percent}%`);
},
});
console.log('Large file uploaded:', result);
}
// 获取签名 URL
export async function getPresignedUrl() {
const url = await ossClient.getSignedUrl('images/test.jpg', 3600);
console.log('Presigned URL:', url);
}
// 批量操作
export async function batchOperations() {
const uploadResults = await ossClient.batchUpload([
{
path: 'images/1.jpg',
file: fs.readFileSync(path.join(__dirname, '../upload/image1.jpg')),
},
{
path: 'images/2.jpg',
file: fs.readFileSync(path.join(__dirname, '../upload/image2.jpg')),
},
]);
console.log('Upload results:', uploadResults);
// 批量删除
await ossClient.batchDelete(['images/1.jpg', 'images/2.jpg']);
}
// 复制和移动文件
export async function copyAndMoveFiles() {
// 复制文件
const copyResult = await ossClient.copy(
'images/test.jpg',
'images/test-copy.jpg',
);
console.log('Copy result:', copyResult);
// 移动文件
await ossClient.move('images/test-copy.jpg', 'images/backup/test-copy.jpg');
}
// 获取文件信息
export async function getFileInformation() {
// 获取文件大小
const size = await ossClient.getFileSize('images/test.jpg');
console.log('File size:', size);
// 获取文件URL
const url = await ossClient.getUrl('images/test.jpg');
console.log('File URL:', url);
// 获取签名URL
const signedUrl = await ossClient.getSignedUrl('images/test.jpg', 3600);
console.log('Signed URL:', signedUrl);
}
// 目录操作
export async function directoryOperations() {
// 创建目录
await ossClient.createDirectory('test/');
// 列出目录
const dirs = await ossClient.listDirectories('');
console.log('Directories:', dirs);
// 删除目录
await ossClient.deleteDirectory('test/');
}贡献
欢迎提交 Issue 和 Pull Request!
许可证
MIT License
