mirror of
https://github.com/Afilmory/afilmory
synced 2026-02-01 22:48:17 +00:00
refactor: remove deprecated storage functions and streamline storage management
- Deleted the builder-storage.mdc file, which contained outdated documentation for the storage abstraction layer. - Removed deprecated functions related to S3 operations from the storage adapters, encouraging the use of the new StorageManager API. - Updated various components to utilize the StorageManager for file retrieval and URL generation, enhancing consistency and maintainability across the codebase. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
@@ -1,128 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# 存储抽象层
|
||||
|
||||
本模块使用适配器模式提供了统一的存储接口,支持多种存储提供商。目前已实现 S3 存储提供商,后续可以方便地扩展其他存储服务。
|
||||
|
||||
## 架构设计
|
||||
|
||||
- **`interfaces.ts`**: 定义通用的存储接口和数据结构
|
||||
- **`providers/`**: 具体的存储提供商实现
|
||||
- **`s3-provider.ts`**: S3 存储提供商实现
|
||||
- **`factory.ts`**: 存储提供商工厂,负责创建具体的存储实例
|
||||
- **`manager.ts`**: 存储管理器,提供统一的存储操作接口
|
||||
- **`adapters.ts`**: 适配器函数,保持与原有 API 的兼容性
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 推荐方式:使用 StorageManager
|
||||
|
||||
```typescript
|
||||
import { defaultStorageManager, StorageManager } from '@/core/storage'
|
||||
|
||||
// 使用默认存储管理器(基于环境变量配置)
|
||||
const buffer = await defaultStorageManager.getFile('path/to/image.jpg')
|
||||
const images = await defaultStorageManager.listImages()
|
||||
const allFiles = await defaultStorageManager.listAllFiles()
|
||||
const publicUrl = defaultStorageManager.generatePublicUrl('path/to/image.jpg')
|
||||
const livePhotos = await defaultStorageManager.detectLivePhotos()
|
||||
|
||||
// 或者创建自定义配置的存储管理器
|
||||
const customManager = new StorageManager({
|
||||
provider: 's3',
|
||||
bucket: 'my-bucket',
|
||||
region: 'us-east-1',
|
||||
endpoint: 'https://s3.amazonaws.com',
|
||||
prefix: 'photos/',
|
||||
customDomain: 'https://cdn.example.com'
|
||||
})
|
||||
```
|
||||
|
||||
### 兼容性方式:使用原有 API
|
||||
|
||||
```typescript
|
||||
// 这些函数仍然可用,但标记为 deprecated
|
||||
import {
|
||||
getImageFromS3,
|
||||
listImagesFromS3,
|
||||
listAllFilesFromS3,
|
||||
detectLivePhotos,
|
||||
generateS3Url
|
||||
} from '@/core/s3/operations'
|
||||
|
||||
const buffer = await getImageFromS3('path/to/image.jpg')
|
||||
const images = await listImagesFromS3()
|
||||
```
|
||||
|
||||
### 直接使用存储提供商
|
||||
|
||||
```typescript
|
||||
import { S3StorageProvider } from '@/core/storage'
|
||||
|
||||
const s3Provider = new S3StorageProvider({
|
||||
provider: 's3',
|
||||
bucket: 'my-bucket',
|
||||
region: 'us-east-1',
|
||||
// ... 其他配置
|
||||
})
|
||||
|
||||
const buffer = await s3Provider.getFile('path/to/image.jpg')
|
||||
```
|
||||
|
||||
## 扩展新的存储提供商
|
||||
|
||||
1. 实现 `StorageProvider` 接口:
|
||||
|
||||
```typescript
|
||||
import { StorageProvider, StorageObject } from '@/core/storage/interfaces'
|
||||
|
||||
export class MyStorageProvider implements StorageProvider {
|
||||
async getFile(key: string, logger?: Logger['s3']): Promise<Buffer | null> {
|
||||
// 实现文件获取逻辑
|
||||
}
|
||||
|
||||
async listImages(): Promise<StorageObject[]> {
|
||||
// 实现图片列表逻辑
|
||||
}
|
||||
|
||||
// ... 实现其他接口方法
|
||||
}
|
||||
```
|
||||
|
||||
2. 在工厂类中注册新的提供商:
|
||||
|
||||
```typescript
|
||||
// 在 factory.ts 中添加新的 case
|
||||
case 'my-storage':
|
||||
return new MyStorageProvider(config)
|
||||
```
|
||||
|
||||
3. 更新 `StorageConfig` 接口的 `provider` 类型。
|
||||
|
||||
## 优势
|
||||
|
||||
1. **解耦**: 业务逻辑与具体存储实现分离
|
||||
2. **可扩展**: 轻松添加新的存储提供商
|
||||
3. **统一接口**: 所有存储操作使用相同的 API
|
||||
4. **向后兼容**: 保持原有 API 的兼容性
|
||||
5. **类型安全**: 完整的 TypeScript 类型支持
|
||||
6. **配置灵活**: 支持多种配置方式
|
||||
|
||||
## 迁移指南
|
||||
|
||||
现有代码可以继续使用原有的 S3 函数,无需立即修改。建议在新代码中使用 `StorageManager` API,并逐步迁移现有代码。
|
||||
|
||||
旧代码:
|
||||
```typescript
|
||||
import { getImageFromS3 } from '@/core/s3/operations'
|
||||
const buffer = await getImageFromS3('key')
|
||||
```
|
||||
|
||||
新代码:
|
||||
```typescript
|
||||
import { defaultStorageManager } from '@/core/storage'
|
||||
const buffer = await defaultStorageManager.getFile('key')
|
||||
```
|
||||
@@ -9,4 +9,5 @@ i18n 编写规范。
|
||||
2. 使用扁平键。使用 `.` 方式分割。不能使用 object 形式嵌套。
|
||||
3. 对单复数敏感的语言应该区分,使用 `_one` 和 `_other` 的形式。
|
||||
4. 在 build 阶段,扁平化的点分隔键(如 'exif.custom.rendered.custom')会自动转换为嵌套的 object 对象,因此可能引发冲突。例如,'exif.custom.rendered.custom' 会与 'exif.custom.rendered' 发生冲突。请避免使用此类点分隔的扁平键。
|
||||
5. @locales 位于根目录,需要同时处理已有的全部的语言
|
||||
5. @locales 位于根目录,需要同时处理已有的全部的语言
|
||||
6. 你需要修改 [en.json](mdc:locales/app/en.json) 然后再修改 [zh-CN.json](mdc:locales/app/zh-CN.json) 和其他语言。因为 eslint 会自动删除其他 json 中的 en 中没有的 key。
|
||||
@@ -26,7 +26,7 @@ export interface BuilderOptions {
|
||||
concurrencyLimit?: number // 可选,如果未提供则使用配置文件中的默认值
|
||||
}
|
||||
|
||||
export class PhotoGalleryBuilder {
|
||||
class PhotoGalleryBuilder {
|
||||
private storageManager: StorageManager
|
||||
private config: BuilderConfig
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export type { BuilderOptions } from './builder.js'
|
||||
export { defaultBuilder, PhotoGalleryBuilder } from './builder.js'
|
||||
export { defaultBuilder } from './builder.js'
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
// 主要构建器
|
||||
export {
|
||||
type BuilderOptions,
|
||||
defaultBuilder,
|
||||
PhotoGalleryBuilder,
|
||||
} from './builder/index.js'
|
||||
export { type BuilderOptions, defaultBuilder } from './builder/index.js'
|
||||
export type { StorageConfig } from './storage/interfaces.js'
|
||||
// 日志系统
|
||||
export { type Logger, logger, type WorkerLogger } from './logger/index.js'
|
||||
@@ -33,11 +29,6 @@ export {
|
||||
thumbnailExists,
|
||||
} from './image/thumbnail.js'
|
||||
export { s3Client } from './s3/client.js'
|
||||
export {
|
||||
generateS3Url,
|
||||
getImageFromS3,
|
||||
listImagesFromS3,
|
||||
} from './s3/operations.js'
|
||||
|
||||
// 照片处理
|
||||
export { extractPhotoInfo } from './photo/info-extractor.js'
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { _Object } from '@aws-sdk/client-s3'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { defaultBuilder } from '../builder/builder.js'
|
||||
import {
|
||||
convertBmpToJpegSharpInstance,
|
||||
getImageMetadataWithSharp,
|
||||
isBitmap,
|
||||
preprocessImageBuffer,
|
||||
} from '../image/processor.js'
|
||||
import { defaultStorageManager } from '../storage/manager.js'
|
||||
import type { PhotoManifestItem } from '../types/photo.js'
|
||||
import {
|
||||
processExifData,
|
||||
@@ -46,7 +46,9 @@ export async function preprocessImage(
|
||||
|
||||
try {
|
||||
// 获取图片数据
|
||||
const rawImageBuffer = await defaultStorageManager.getFile(photoKey)
|
||||
const rawImageBuffer = await defaultBuilder
|
||||
.getStorageManager()
|
||||
.getFile(photoKey)
|
||||
if (!rawImageBuffer) {
|
||||
loggers.image.error(`无法获取图片数据:${photoKey}`)
|
||||
return null
|
||||
@@ -199,7 +201,9 @@ export async function executePhotoProcessingPipeline(
|
||||
dateTaken: photoInfo.dateTaken,
|
||||
views: photoInfo.views,
|
||||
tags: photoInfo.tags,
|
||||
originalUrl: defaultStorageManager.generatePublicUrl(photoKey),
|
||||
originalUrl: defaultBuilder
|
||||
.getStorageManager()
|
||||
.generatePublicUrl(photoKey),
|
||||
thumbnailUrl: thumbnailResult.thumbnailUrl,
|
||||
blurhash: thumbnailResult.blurhash,
|
||||
width: metadata.width,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { _Object } from '@aws-sdk/client-s3'
|
||||
|
||||
import { defaultBuilder } from '../builder/builder.js'
|
||||
import type { StorageObject } from '../storage/interfaces.js'
|
||||
import { defaultStorageManager } from '../storage/manager.js'
|
||||
import { getGlobalLoggers } from './logger-adapter.js'
|
||||
|
||||
export interface LivePhotoResult {
|
||||
@@ -40,7 +40,9 @@ export function processLivePhoto(
|
||||
return { isLivePhoto: false }
|
||||
}
|
||||
|
||||
const livePhotoVideoUrl = defaultStorageManager.generatePublicUrl(videoKey)
|
||||
const livePhotoVideoUrl = defaultBuilder
|
||||
.getStorageManager()
|
||||
.generatePublicUrl(videoKey)
|
||||
|
||||
loggers.image.info(`📱 检测到 Live Photo:${photoKey} -> ${videoKey}`)
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import type { _Object } from '@aws-sdk/client-s3'
|
||||
|
||||
import type { Logger } from '../logger/index.js'
|
||||
import type { StorageObject } from './interfaces.js'
|
||||
import { defaultStorageManager } from './manager.js'
|
||||
|
||||
// 将 StorageObject 转换为 _Object 以保持兼容性
|
||||
function convertStorageObjectToS3Object(storageObject: StorageObject): _Object {
|
||||
return {
|
||||
Key: storageObject.key,
|
||||
Size: storageObject.size,
|
||||
LastModified: storageObject.lastModified,
|
||||
ETag: storageObject.etag,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 S3 获取图片(兼容性函数)
|
||||
* @deprecated 推荐使用 defaultStorageManager.getFile()
|
||||
*/
|
||||
export async function getImageFromS3(
|
||||
key: string,
|
||||
s3Logger?: Logger['s3'],
|
||||
): Promise<Buffer | null> {
|
||||
return defaultStorageManager.getFile(key, s3Logger)
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出 S3 中的所有图片文件(兼容性函数)
|
||||
* @deprecated 推荐使用 defaultStorageManager.listImages()
|
||||
*/
|
||||
export async function listImagesFromS3(): Promise<_Object[]> {
|
||||
const storageObjects = await defaultStorageManager.listImages()
|
||||
return storageObjects.map((obj) => convertStorageObjectToS3Object(obj))
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出 S3 中的所有文件(兼容性函数)
|
||||
* @deprecated 推荐使用 defaultStorageManager.listAllFiles()
|
||||
*/
|
||||
export async function listAllFilesFromS3(): Promise<_Object[]> {
|
||||
const storageObjects = await defaultStorageManager.listAllFiles()
|
||||
return storageObjects.map((obj) => convertStorageObjectToS3Object(obj))
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测 live photo 配对(兼容性函数)
|
||||
* @deprecated 推荐使用 defaultStorageManager.detectLivePhotos()
|
||||
*/
|
||||
export function detectLivePhotos(allObjects: _Object[]): Map<string, _Object> {
|
||||
// 转换 _Object 数组为 StorageObject 数组
|
||||
const storageObjects: StorageObject[] = allObjects.map((obj) => ({
|
||||
key: obj.Key || '',
|
||||
size: obj.Size,
|
||||
lastModified: obj.LastModified,
|
||||
etag: obj.ETag,
|
||||
}))
|
||||
|
||||
// 使用存储管理器检测 Live Photos
|
||||
const livePhotoMap = defaultStorageManager
|
||||
.getProvider()
|
||||
.detectLivePhotos(storageObjects)
|
||||
|
||||
// 转换回 _Object 格式
|
||||
const result = new Map<string, _Object>()
|
||||
for (const [key, storageObject] of livePhotoMap) {
|
||||
result.set(key, convertStorageObjectToS3Object(storageObject))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 S3 公共 URL(兼容性函数)
|
||||
* @deprecated 推荐使用 defaultStorageManager.generatePublicUrl()
|
||||
*/
|
||||
export function generateS3Url(key: string): string {
|
||||
return defaultStorageManager.generatePublicUrl(key)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { env } from '@env'
|
||||
|
||||
import type { StorageConfig, StorageProvider } from './interfaces'
|
||||
import { GitHubStorageProvider } from './providers/github-provider.js'
|
||||
import { S3StorageProvider } from './providers/s3-provider.js'
|
||||
@@ -20,23 +18,4 @@ export class StorageFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于环境变量创建默认的存储提供商
|
||||
* @returns 默认存储提供商实例
|
||||
*/
|
||||
static createDefaultProvider(): StorageProvider {
|
||||
const config: StorageConfig = {
|
||||
provider: 's3',
|
||||
bucket: env.S3_BUCKET_NAME,
|
||||
region: env.S3_REGION,
|
||||
endpoint: env.S3_ENDPOINT,
|
||||
accessKeyId: env.S3_ACCESS_KEY_ID,
|
||||
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
|
||||
prefix: env.S3_PREFIX,
|
||||
customDomain: env.S3_CUSTOM_DOMAIN,
|
||||
}
|
||||
|
||||
return this.createProvider(config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export type {
|
||||
export { StorageFactory } from './factory.js'
|
||||
|
||||
// 导出管理器
|
||||
export { defaultStorageManager, StorageManager } from './manager.js'
|
||||
export { StorageManager } from './manager.js'
|
||||
|
||||
// 导出具体提供商(如果需要直接使用)
|
||||
export { GitHubStorageProvider } from './providers/github-provider.js'
|
||||
|
||||
@@ -9,10 +9,8 @@ import type {
|
||||
export class StorageManager {
|
||||
private provider: StorageProvider
|
||||
|
||||
constructor(config?: StorageConfig) {
|
||||
this.provider = config
|
||||
? StorageFactory.createProvider(config)
|
||||
: StorageFactory.createDefaultProvider()
|
||||
constructor(config: StorageConfig) {
|
||||
this.provider = StorageFactory.createProvider(config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,6 +76,3 @@ export class StorageManager {
|
||||
this.provider = StorageFactory.createProvider(config)
|
||||
}
|
||||
}
|
||||
|
||||
// 导出默认的存储管理器实例
|
||||
export const defaultStorageManager = new StorageManager()
|
||||
|
||||
Reference in New Issue
Block a user