280 lines
6.3 KiB
Markdown
280 lines
6.3 KiB
Markdown
# 文件访问使用指南
|
||
|
||
本文档说明如何使用 `@repo/storage` 包提供的文件访问功能。
|
||
|
||
## 功能概述
|
||
|
||
存储包提供统一的文件访问接口:
|
||
|
||
- **统一下载接口** (`/download/:fileId`) - 适用于所有存储类型,提供统一的文件访问
|
||
|
||
## 使用方法
|
||
|
||
### 1. 基础配置
|
||
|
||
```typescript
|
||
import { createStorageApp } from '@repo/storage';
|
||
|
||
// 创建包含所有功能的存储应用
|
||
const storageApp = createStorageApp({
|
||
apiBasePath: '/api/storage', // API 管理接口
|
||
uploadPath: '/upload', // TUS 上传接口
|
||
downloadPath: '/download', // 文件下载接口
|
||
});
|
||
|
||
app.route('/', storageApp);
|
||
```
|
||
|
||
### 2. 分别配置功能
|
||
|
||
```typescript
|
||
import { createStorageRoutes, createTusUploadRoutes, createFileDownloadRoutes } from '@repo/storage';
|
||
|
||
const app = new Hono();
|
||
|
||
// 存储管理 API
|
||
app.route('/api/storage', createStorageRoutes());
|
||
|
||
// 文件上传
|
||
app.route('/upload', createTusUploadRoutes());
|
||
|
||
// 文件下载(所有存储类型)
|
||
app.route('/download', createFileDownloadRoutes());
|
||
```
|
||
|
||
## 文件访问方式
|
||
|
||
### 统一下载接口
|
||
|
||
无论使用哪种存储类型,都通过统一的下载接口访问文件:
|
||
|
||
```bash
|
||
# 访问文件(支持内联显示和下载)
|
||
GET http://localhost:3000/download/2024/01/01/abc123/image.jpg
|
||
GET http://localhost:3000/download/2024/01/01/abc123/document.pdf
|
||
```
|
||
|
||
### 本地存储
|
||
|
||
当 `STORAGE_TYPE=local` 时:
|
||
|
||
- 下载接口直接读取本地文件
|
||
- 自动设置正确的 Content-Type
|
||
- 支持内联显示(`Content-Disposition: inline`)
|
||
|
||
### S3 存储
|
||
|
||
当 `STORAGE_TYPE=s3` 时:
|
||
|
||
- 下载接口重定向到 S3 URL
|
||
- 也可以直接访问 S3 URL(如果存储桶是公开的)
|
||
|
||
```bash
|
||
# 直接访问 S3 URL(如果存储桶是公开的)
|
||
GET https://bucket.s3.region.amazonaws.com/2024/01/01/abc123/file.jpg
|
||
```
|
||
|
||
## 代码示例
|
||
|
||
### 生成文件访问 URL
|
||
|
||
```typescript
|
||
import { StorageUtils } from '@repo/storage';
|
||
|
||
const storageUtils = StorageUtils.getInstance();
|
||
|
||
// 生成文件访问 URL
|
||
function getFileUrl(fileId: string) {
|
||
// 结果: http://localhost:3000/download/2024/01/01/abc123/file.jpg
|
||
return storageUtils.generateFileUrl(fileId);
|
||
}
|
||
|
||
// 生成完整的公开访问 URL
|
||
function getPublicFileUrl(fileId: string) {
|
||
// 结果: https://yourdomain.com/download/2024/01/01/abc123/file.jpg
|
||
return storageUtils.generateFileUrl(fileId, 'https://yourdomain.com');
|
||
}
|
||
|
||
// 生成 S3 直接访问 URL(仅 S3 存储)
|
||
function getDirectUrl(fileId: string) {
|
||
try {
|
||
// S3 存储: https://bucket.s3.region.amazonaws.com/2024/01/01/abc123/file.jpg
|
||
return storageUtils.generateDirectUrl(fileId);
|
||
} catch (error) {
|
||
// 本地存储会抛出错误,使用下载接口
|
||
return storageUtils.generateFileUrl(fileId);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 在 React 组件中使用
|
||
|
||
```tsx
|
||
import { useState, useEffect } from 'react';
|
||
|
||
function FileDisplay({ fileId }: { fileId: string }) {
|
||
const [fileUrl, setFileUrl] = useState<string>('');
|
||
|
||
useEffect(() => {
|
||
// 获取文件访问 URL
|
||
fetch(`/api/storage/resource/${fileId}`)
|
||
.then((res) => res.json())
|
||
.then((data) => {
|
||
if (data.status === 'ready' && data.resource) {
|
||
// 生成文件访问 URL
|
||
const url = `/download/${fileId}`;
|
||
setFileUrl(url);
|
||
}
|
||
});
|
||
}, [fileId]);
|
||
|
||
if (!fileUrl) return <div>Loading...</div>;
|
||
|
||
return (
|
||
<div>
|
||
{/* 图片会内联显示 */}
|
||
<img src={fileUrl} alt="Uploaded file" />
|
||
|
||
{/* 下载链接 */}
|
||
<a href={fileUrl} download>
|
||
下载文件
|
||
</a>
|
||
|
||
{/* PDF 等文档可以在新窗口打开 */}
|
||
<a href={fileUrl} target="_blank" rel="noopener noreferrer">
|
||
在新窗口打开
|
||
</a>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 文件类型处理
|
||
|
||
```typescript
|
||
function getFileDisplayUrl(fileId: string, mimeType: string) {
|
||
const baseUrl = `/download/${fileId}`;
|
||
|
||
// 根据文件类型决定显示方式
|
||
if (mimeType.startsWith('image/')) {
|
||
// 图片直接显示
|
||
return baseUrl;
|
||
} else if (mimeType === 'application/pdf') {
|
||
// PDF 可以内联显示
|
||
return baseUrl;
|
||
} else {
|
||
// 其他文件类型强制下载
|
||
return `${baseUrl}?download=true`;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 安全考虑
|
||
|
||
### 1. 访问控制
|
||
|
||
如需要权限验证,可以添加认证中间件:
|
||
|
||
```typescript
|
||
import { createFileDownloadRoutes } from '@repo/storage';
|
||
|
||
const app = new Hono();
|
||
|
||
// 添加认证中间件
|
||
app.use('/download/*', async (c, next) => {
|
||
// 检查用户权限
|
||
const token = c.req.header('Authorization');
|
||
if (!isValidToken(token)) {
|
||
return c.json({ error: 'Unauthorized' }, 401);
|
||
}
|
||
await next();
|
||
});
|
||
|
||
// 添加文件下载服务
|
||
app.route('/download', createFileDownloadRoutes());
|
||
```
|
||
|
||
### 2. 文件类型限制
|
||
|
||
```typescript
|
||
app.use('/download/*', async (c, next) => {
|
||
const fileId = c.req.param('fileId');
|
||
|
||
// 从数据库获取文件信息
|
||
const { resource } = await getResourceByFileId(fileId);
|
||
if (!resource) {
|
||
return c.json({ error: 'File not found' }, 404);
|
||
}
|
||
|
||
// 检查文件类型
|
||
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
|
||
if (!allowedTypes.includes(resource.mimeType)) {
|
||
return c.json({ error: 'File type not allowed' }, 403);
|
||
}
|
||
|
||
await next();
|
||
});
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
### 1. 缓存设置
|
||
|
||
```typescript
|
||
app.use('/download/*', async (c, next) => {
|
||
await next();
|
||
|
||
// 设置缓存头
|
||
c.header('Cache-Control', 'public, max-age=31536000'); // 1年
|
||
c.header('ETag', generateETag(c.req.path));
|
||
});
|
||
```
|
||
|
||
### 2. CDN 配置
|
||
|
||
对于生产环境,建议使用 CDN:
|
||
|
||
```typescript
|
||
import { StorageUtils } from '@repo/storage';
|
||
|
||
const storageUtils = StorageUtils.getInstance();
|
||
|
||
// 使用 CDN 域名
|
||
const cdnUrl = 'https://cdn.yourdomain.com';
|
||
const fileUrl = storageUtils.generateFileUrl(fileId, cdnUrl);
|
||
```
|
||
|
||
## 故障排除
|
||
|
||
### 常见问题
|
||
|
||
1. **404 文件未找到**
|
||
|
||
- 检查文件是否存在于数据库
|
||
- 确认文件路径是否正确
|
||
- 检查文件权限(本地存储)
|
||
|
||
2. **下载接口不工作**
|
||
|
||
- 检查路由配置
|
||
- 确认存储配置正确
|
||
- 查看服务器日志
|
||
|
||
3. **S3 文件无法访问**
|
||
- 检查 S3 存储桶权限
|
||
- 确认文件是否上传成功
|
||
- 验证 S3 配置是否正确
|
||
|
||
### 调试方法
|
||
|
||
```bash
|
||
# 检查文件是否存在
|
||
curl -I http://localhost:3000/download/2024/01/01/abc123/file.jpg
|
||
|
||
# 检查存储配置
|
||
curl http://localhost:3000/api/storage/storage/info
|
||
|
||
# 检查文件信息
|
||
curl http://localhost:3000/api/storage/resource/2024/01/01/abc123/file.jpg
|
||
```
|