mirror of
https://github.com/RhenCloud/Cloud-Index.git
synced 2025-12-06 15:26:10 +08:00
feat: 更新文件预览功能,并优化不支持文件的提示信息
This commit is contained in:
123
PREVIEW_FEATURE.md
Normal file
123
PREVIEW_FEATURE.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# 文件预览功能说明
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
文件预览功能已更新,现在支持多种文件类型的在线预览,并对不支持预览的文件类型提供友好的提示信息。
|
||||||
|
|
||||||
|
## 支持的文件类型
|
||||||
|
|
||||||
|
### 1. 图片文件
|
||||||
|
|
||||||
|
支持的格式:`jpg`, `jpeg`, `png`, `gif`, `bmp`, `webp`, `svg`, `ico`
|
||||||
|
|
||||||
|
**预览方式**:在模态框中直接显示图片,支持缩放适配。
|
||||||
|
|
||||||
|
### 2. 视频文件
|
||||||
|
|
||||||
|
支持的格式:`mp4`, `webm`, `ogg`, `mov`, `avi`, `mkv`, `m4v`
|
||||||
|
|
||||||
|
**预览方式**:使用 HTML5 视频播放器,支持播放控制、音量调节、全屏等功能。
|
||||||
|
|
||||||
|
### 3. 音频文件 ⭐ 新增
|
||||||
|
|
||||||
|
支持的格式:`mp3`, `wav`, `ogg`, `m4a`, `aac`, `flac`, `opus`, `weba`
|
||||||
|
|
||||||
|
**预览方式**:显示音乐图标和 HTML5 音频播放器,支持播放控制、进度条、音量调节。
|
||||||
|
|
||||||
|
### 4. PDF 文件 ⭐ 新增
|
||||||
|
|
||||||
|
支持的格式:`pdf`
|
||||||
|
|
||||||
|
**预览方式**:使用 iframe 内嵌 PDF 查看器,支持页面导航和缩放(取决于浏览器)。
|
||||||
|
|
||||||
|
### 5. 文本文件 ⭐ 新增
|
||||||
|
|
||||||
|
支持的格式:
|
||||||
|
|
||||||
|
- 纯文本:`txt`, `log`, `md`
|
||||||
|
- 数据格式:`json`, `xml`, `csv`, `yaml`, `yml`, `toml`, `ini`, `conf`
|
||||||
|
- 代码文件:`js`, `css`, `html`, `py`, `java`, `c`, `cpp`, `h`, `hpp`, `sh`, `bat`
|
||||||
|
|
||||||
|
**预览方式**:以等宽字体显示文本内容,保留格式和换行,支持滚动查看。
|
||||||
|
|
||||||
|
## 不支持预览的文件
|
||||||
|
|
||||||
|
对于不在上述类型中的文件(如:zip、exe、docx、xlsx 等),系统会显示一个友好的提示界面,包含:
|
||||||
|
|
||||||
|
- 文件图标
|
||||||
|
- "该文件不支持预览" 提示信息
|
||||||
|
- 文件名显示
|
||||||
|
- **下载文件** 按钮 - 直接下载文件到本地
|
||||||
|
- **新窗口打开** 按钮 - 在新标签页中尝试打开文件
|
||||||
|
|
||||||
|
## 用户体验改进
|
||||||
|
|
||||||
|
1. **统一的预览界面**:所有支持预览的文件都在同一个模态框中显示
|
||||||
|
2. **加载提示**:预览加载时显示 "加载中..." 提示
|
||||||
|
3. **错误处理**:加载失败时显示友好的错误信息
|
||||||
|
4. **快捷操作**:
|
||||||
|
- 点击预览窗口外部区域可关闭预览
|
||||||
|
- 按 `Esc` 键关闭预览
|
||||||
|
- 预览窗口内提供下载和关闭按钮
|
||||||
|
5. **响应式设计**:在移动设备上自动调整预览窗口大小
|
||||||
|
6. **主题适配**:文本预览支持深色/浅色主题切换
|
||||||
|
|
||||||
|
## 技术实现
|
||||||
|
|
||||||
|
### JavaScript 更新
|
||||||
|
|
||||||
|
- `getFileType(filename)` 函数:扩展了文件类型识别,支持 5 大类文件
|
||||||
|
- `openPreview(url, filename)` 函数:
|
||||||
|
- 根据文件类型动态创建相应的预览元素
|
||||||
|
- 为不支持的文件显示操作提示界面
|
||||||
|
- 处理加载成功和失败的各种情况
|
||||||
|
|
||||||
|
### CSS 样式
|
||||||
|
|
||||||
|
新增样式类:
|
||||||
|
|
||||||
|
- `.preview-audio-wrapper` - 音频预览容器
|
||||||
|
- `.preview-audio` - 音频播放器
|
||||||
|
- `.preview-text` - 文本内容显示
|
||||||
|
- `.preview-unsupported` - 不支持预览的提示界面
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 预览音频文件
|
||||||
|
|
||||||
|
```
|
||||||
|
点击任意 .mp3, .wav 等音频文件的 "预览" 按钮
|
||||||
|
→ 显示音乐图标和播放控制器
|
||||||
|
→ 自动开始播放
|
||||||
|
```
|
||||||
|
|
||||||
|
### 预览文本文件
|
||||||
|
|
||||||
|
```
|
||||||
|
点击 .txt, .json, .py 等文本文件的 "预览" 按钮
|
||||||
|
→ 加载并显示文件内容
|
||||||
|
→ 使用等宽字体,便于阅读代码
|
||||||
|
```
|
||||||
|
|
||||||
|
### 处理不支持的文件
|
||||||
|
|
||||||
|
```
|
||||||
|
点击 .zip, .exe 等文件的 "预览" 按钮
|
||||||
|
→ 显示提示界面
|
||||||
|
→ 提供下载或新窗口打开选项
|
||||||
|
```
|
||||||
|
|
||||||
|
## 浏览器兼容性
|
||||||
|
|
||||||
|
- **现代浏览器**:完全支持(Chrome, Firefox, Safari, Edge)
|
||||||
|
- **音频格式**:取决于浏览器对各种音频编码的支持
|
||||||
|
- **PDF 预览**:取决于浏览器内置 PDF 查看器
|
||||||
|
- **视频格式**:建议使用 mp4 (H.264) 以获得最佳兼容性
|
||||||
|
|
||||||
|
## 未来改进方向
|
||||||
|
|
||||||
|
- [ ] 添加代码语法高亮
|
||||||
|
- [ ] 支持 Office 文档预览(需要第三方服务)
|
||||||
|
- [ ] 添加图片旋转、缩放控制
|
||||||
|
- [ ] 支持预览窗口内文件导航(上一个/下一个)
|
||||||
|
- [ ] 添加预览历史记录
|
||||||
66
README.md
66
README.md
@@ -1,6 +1,7 @@
|
|||||||
# Cloud-Index
|
# Cloud-Index
|
||||||
|
|
||||||
一个支持多种云存储后端的文件管理、索引和浏览服务。
|
一个支持多种云存储后端的文件管理、索引和浏览服务。
|
||||||
|
更多详细信息请访问 [项目文档](https://docs.cloud-index.rhen.cloud)
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
|
|
||||||
@@ -70,20 +71,18 @@ cp .env.example .env
|
|||||||
python app.py
|
python app.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
### Vercel 部署
|
||||||
|
|
||||||
|
项目包含 `vercel.json` 配置文件,可直接部署到 Vercel:
|
||||||
|
|
||||||
|
1. 在 Vercel 中导入项目
|
||||||
|
2. 在 Vercel 项目设置中配置环境变量
|
||||||
|
3. 部署
|
||||||
|
|
||||||
## 配置说明
|
## 配置说明
|
||||||
|
|
||||||
### 选择存储类型
|
|
||||||
|
|
||||||
在 `.env` 文件中设置 `STORAGE_TYPE` 来选择存储后端:
|
|
||||||
|
|
||||||
```env
|
|
||||||
# 使用 Cloudflare R2
|
|
||||||
STORAGE_TYPE=r2
|
|
||||||
|
|
||||||
# 或使用腾讯云 cnb.cool
|
|
||||||
STORAGE_TYPE=cnbcool
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cloudflare R2 配置
|
### Cloudflare R2 配置
|
||||||
|
|
||||||
```env
|
```env
|
||||||
@@ -126,7 +125,7 @@ GITHUB_BRANCH=main
|
|||||||
# GitHub Raw 文件反向代理 URL(可选,用于加速访问)
|
# GitHub Raw 文件反向代理 URL(可选,用于加速访问)
|
||||||
# 常用反向代理:
|
# 常用反向代理:
|
||||||
# - https://raw.fastgit.org (推荐,速度快)
|
# - https://raw.fastgit.org (推荐,速度快)
|
||||||
# - https://ghproxy.com/https://raw.githubusercontent.com (需要拼接路径)
|
# - https://ghproxy.com
|
||||||
# - https://raw.kgithub.com
|
# - https://raw.kgithub.com
|
||||||
# 留空则使用官方 raw.githubusercontent.com(国内可能较慢)
|
# 留空则使用官方 raw.githubusercontent.com(国内可能较慢)
|
||||||
GITHUB_RAW_PROXY_URL=https://raw.fastgit.org
|
GITHUB_RAW_PROXY_URL=https://raw.fastgit.org
|
||||||
@@ -144,7 +143,7 @@ cloud-index/
|
|||||||
│ ├── base.py # 基础存储类(抽象类)
|
│ ├── base.py # 基础存储类(抽象类)
|
||||||
│ ├── factory.py # 存储工厂类
|
│ ├── factory.py # 存储工厂类
|
||||||
│ ├── r2.py # Cloudflare R2 实现
|
│ ├── r2.py # Cloudflare R2 实现
|
||||||
│ └── cnbcool.py # 腾讯云 cnb.cool 实现
|
│ └── github.py # GitHub Repository 实现
|
||||||
├── templates/ # HTML 模板
|
├── templates/ # HTML 模板
|
||||||
│ ├── index.html
|
│ ├── index.html
|
||||||
│ └── footer.html
|
│ └── footer.html
|
||||||
@@ -154,21 +153,6 @@ cloud-index/
|
|||||||
└── requirements.txt # Python 依赖
|
└── requirements.txt # Python 依赖
|
||||||
```
|
```
|
||||||
|
|
||||||
## 贡献
|
|
||||||
|
|
||||||
项目采用策略模式和工厂模式,使得添加新的存储后端变得简单:
|
|
||||||
|
|
||||||
1. **BaseStorage** - 定义存储后端的统一接口
|
|
||||||
2. **具体实现** (R2Storage, CnbCoolStorage) - 实现具体的存储逻辑
|
|
||||||
3. **StorageFactory** - 根据配置创建对应的存储实例
|
|
||||||
|
|
||||||
### 添加新的存储后端
|
|
||||||
|
|
||||||
1. 在 `storages/` 目录下创建新的存储实现文件
|
|
||||||
2. 继承 `BaseStorage` 并实现所有抽象方法
|
|
||||||
3. 在 `StorageFactory` 中添加对应的创建逻辑
|
|
||||||
4. 更新 `.env.example` 添加新的配置项
|
|
||||||
|
|
||||||
## API 路由
|
## API 路由
|
||||||
|
|
||||||
- `GET /` - 浏览根目录
|
- `GET /` - 浏览根目录
|
||||||
@@ -186,16 +170,6 @@ cloud-index/
|
|||||||
|
|
||||||
详细 API 文档:[API 文档](docs/api.md)
|
详细 API 文档:[API 文档](docs/api.md)
|
||||||
|
|
||||||
## 部署
|
|
||||||
|
|
||||||
### Vercel 部署
|
|
||||||
|
|
||||||
项目包含 `vercel.json` 配置文件,可直接部署到 Vercel:
|
|
||||||
|
|
||||||
1. 在 Vercel 中导入项目
|
|
||||||
2. 在 Vercel 项目设置中配置环境变量
|
|
||||||
3. 部署
|
|
||||||
|
|
||||||
### 本地开发
|
### 本地开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -265,7 +239,7 @@ A: 当前支持:
|
|||||||
|
|
||||||
- Cloudflare R2(推荐)
|
- Cloudflare R2(推荐)
|
||||||
- Amazon S3
|
- Amazon S3
|
||||||
- GitHub Repository(通过 cnb.cool)
|
- GitHub Repository
|
||||||
|
|
||||||
### Q: 如何添加新的存储后端?
|
### Q: 如何添加新的存储后端?
|
||||||
|
|
||||||
@@ -273,6 +247,18 @@ A: 参考项目结构中的"添加新的存储后端"部分,继承 `BaseStorag
|
|||||||
|
|
||||||
## 贡献指南
|
## 贡献指南
|
||||||
|
|
||||||
|
项目采用策略模式和工厂模式,使得添加新的存储后端变得简单:
|
||||||
|
|
||||||
|
1. **BaseStorage** - 定义存储后端的统一接口
|
||||||
|
2. **具体实现** (R2Storage, GithubStorage) - 实现具体的存储逻辑
|
||||||
|
3. **StorageFactory** - 根据配置创建对应的存储实例
|
||||||
|
|
||||||
|
### 添加新的存储后端
|
||||||
|
|
||||||
|
1. 在 `storages/` 目录下创建新的存储实现文件
|
||||||
|
2. 继承 `BaseStorage` 并实现所有抽象方法
|
||||||
|
3. 在 `StorageFactory` 中添加对应的创建逻辑
|
||||||
|
4. 更新 `.env.example` 添加新的配置项
|
||||||
欢迎提交 Issue 和 Pull Request!
|
欢迎提交 Issue 和 Pull Request!
|
||||||
|
|
||||||
1. Fork 项目
|
1. Fork 项目
|
||||||
|
|||||||
@@ -534,6 +534,74 @@ video.preview-content {
|
|||||||
background-color: #000;
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 音频预览样式 */
|
||||||
|
.preview-audio-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 12px;
|
||||||
|
min-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-audio {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文本预览样式 */
|
||||||
|
.preview-text {
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
color: #2c3e50;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 85vh;
|
||||||
|
overflow: auto;
|
||||||
|
font-family: "Courier New", Courier, monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .preview-text {
|
||||||
|
background-color: rgba(45, 45, 45, 0.95);
|
||||||
|
color: #e1e1e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 不支持预览的文件样式 */
|
||||||
|
.preview-unsupported {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 40px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
border-radius: 12px;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-unsupported p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-unsupported .action-link {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-unsupported .action-link:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
border-color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.preview-container {
|
.preview-container {
|
||||||
max-width: 95%;
|
max-width: 95%;
|
||||||
@@ -554,6 +622,21 @@ video.preview-content {
|
|||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-audio-wrapper {
|
||||||
|
min-width: 300px;
|
||||||
|
padding: 30px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-unsupported {
|
||||||
|
min-width: 300px;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-text {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 上传进度提示 */
|
/* 上传进度提示 */
|
||||||
|
|||||||
@@ -843,6 +843,32 @@
|
|||||||
const extension = filename.toLowerCase().split(".").pop();
|
const extension = filename.toLowerCase().split(".").pop();
|
||||||
const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "ico"];
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "ico"];
|
||||||
const videoExtensions = ["mp4", "webm", "ogg", "mov", "avi", "mkv", "m4v"];
|
const videoExtensions = ["mp4", "webm", "ogg", "mov", "avi", "mkv", "m4v"];
|
||||||
|
const audioExtensions = ["mp3", "wav", "ogg", "m4a", "aac", "flac", "opus", "weba"];
|
||||||
|
const pdfExtensions = ["pdf"];
|
||||||
|
const textExtensions = [
|
||||||
|
"txt",
|
||||||
|
"log",
|
||||||
|
"md",
|
||||||
|
"json",
|
||||||
|
"xml",
|
||||||
|
"csv",
|
||||||
|
"js",
|
||||||
|
"css",
|
||||||
|
"html",
|
||||||
|
"py",
|
||||||
|
"java",
|
||||||
|
"c",
|
||||||
|
"cpp",
|
||||||
|
"h",
|
||||||
|
"hpp",
|
||||||
|
"sh",
|
||||||
|
"bat",
|
||||||
|
"yaml",
|
||||||
|
"yml",
|
||||||
|
"toml",
|
||||||
|
"ini",
|
||||||
|
"conf",
|
||||||
|
];
|
||||||
|
|
||||||
if (imageExtensions.includes(extension)) {
|
if (imageExtensions.includes(extension)) {
|
||||||
return "image";
|
return "image";
|
||||||
@@ -850,7 +876,16 @@
|
|||||||
if (videoExtensions.includes(extension)) {
|
if (videoExtensions.includes(extension)) {
|
||||||
return "video";
|
return "video";
|
||||||
}
|
}
|
||||||
return "unknown";
|
if (audioExtensions.includes(extension)) {
|
||||||
|
return "audio";
|
||||||
|
}
|
||||||
|
if (pdfExtensions.includes(extension)) {
|
||||||
|
return "pdf";
|
||||||
|
}
|
||||||
|
if (textExtensions.includes(extension)) {
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
return "unsupported";
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPreview(url, filename) {
|
function openPreview(url, filename) {
|
||||||
@@ -865,8 +900,26 @@
|
|||||||
|
|
||||||
const fileType = getFileType(filename);
|
const fileType = getFileType(filename);
|
||||||
|
|
||||||
if (fileType === "unknown") {
|
// 对于不支持预览的文件类型,显示提示信息
|
||||||
window.open(url, "_blank");
|
if (fileType === "unsupported") {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="preview-unsupported">
|
||||||
|
<i class="fas fa-file-alt" style="font-size: 64px; color: var(--text-secondary); margin-bottom: 16px;"></i>
|
||||||
|
<p style="font-size: 18px; margin-bottom: 8px;">该文件不支持预览</p>
|
||||||
|
<p style="color: var(--text-secondary); margin-bottom: 24px;">文件名: ${filename}</p>
|
||||||
|
<div style="display: flex; gap: 12px; justify-content: center;">
|
||||||
|
<button class="action-link" onclick="downloadFile('${url}', '${filename}'); closePreview();" style="padding: 8px 16px;">
|
||||||
|
<i class="fas fa-download"></i> 下载文件
|
||||||
|
</button>
|
||||||
|
<button class="action-link" onclick="window.open('${url}', '_blank'); closePreview();" style="padding: 8px 16px;">
|
||||||
|
<i class="fas fa-external-link-alt"></i> 新窗口打开
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
info.textContent = filename;
|
||||||
|
modal.classList.add("show");
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,6 +958,46 @@
|
|||||||
video.onerror = () => {
|
video.onerror = () => {
|
||||||
container.innerHTML = '<div class="preview-error">视频加载失败</div>';
|
container.innerHTML = '<div class="preview-error">视频加载失败</div>';
|
||||||
};
|
};
|
||||||
|
} else if (fileType === "audio") {
|
||||||
|
const audioWrapper = document.createElement("div");
|
||||||
|
audioWrapper.className = "preview-audio-wrapper";
|
||||||
|
audioWrapper.innerHTML = `
|
||||||
|
<i class="fas fa-music" style="font-size: 64px; color: var(--primary-color); margin-bottom: 24px;"></i>
|
||||||
|
<audio class="preview-audio" controls autoplay>
|
||||||
|
<source src="${url}" type="audio/mpeg">
|
||||||
|
您的浏览器不支持音频播放
|
||||||
|
</audio>
|
||||||
|
`;
|
||||||
|
container.innerHTML = "";
|
||||||
|
container.appendChild(audioWrapper);
|
||||||
|
} else if (fileType === "pdf") {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
iframe.className = "preview-content";
|
||||||
|
iframe.src = url;
|
||||||
|
iframe.style.width = "100%";
|
||||||
|
iframe.style.height = "100%";
|
||||||
|
iframe.style.border = "none";
|
||||||
|
|
||||||
|
container.innerHTML = "";
|
||||||
|
container.appendChild(iframe);
|
||||||
|
} else if (fileType === "text") {
|
||||||
|
container.innerHTML = '<div class="preview-loading">加载文本内容...</div>';
|
||||||
|
|
||||||
|
fetch(url)
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) throw new Error("加载失败");
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then((text) => {
|
||||||
|
const pre = document.createElement("pre");
|
||||||
|
pre.className = "preview-text";
|
||||||
|
pre.textContent = text;
|
||||||
|
container.innerHTML = "";
|
||||||
|
container.appendChild(pre);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
container.innerHTML = '<div class="preview-error">文本加载失败: ' + error.message + "</div>";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ from .factory import StorageFactory
|
|||||||
from .github import GitHubStorage
|
from .github import GitHubStorage
|
||||||
from .r2 import R2Storage
|
from .r2 import R2Storage
|
||||||
|
|
||||||
__all__ = ["BaseStorage", "R2Storage", "CnbCoolStorage", "StorageFactory"]
|
__all__ = ["BaseStorage", "R2Storage", "GitHubStorage", "StorageFactory"]
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ from typing import Optional
|
|||||||
import dotenv
|
import dotenv
|
||||||
|
|
||||||
from .base import BaseStorage
|
from .base import BaseStorage
|
||||||
|
|
||||||
# from .cnbcool import CnbCoolStorage
|
|
||||||
from .github import GitHubStorage
|
from .github import GitHubStorage
|
||||||
from .r2 import R2Storage
|
from .r2 import R2Storage
|
||||||
|
|
||||||
@@ -39,12 +37,9 @@ class StorageFactory:
|
|||||||
cls._instance = R2Storage()
|
cls._instance = R2Storage()
|
||||||
elif storage_type == "github":
|
elif storage_type == "github":
|
||||||
cls._instance = GitHubStorage()
|
cls._instance = GitHubStorage()
|
||||||
# elif storage_type == "cnbcool":
|
|
||||||
# cls._instance = CnbCoolStorage()
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Unsupported storage type: {storage_type}. "
|
f"Unsupported storage type: {storage_type}. Supported types: r2, github"
|
||||||
f"Supported types: r2, github, cnbcool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|||||||
Reference in New Issue
Block a user