fenghuo/docs/STATIC_FILES.md

280 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 文件访问使用指南
本文档说明如何使用 `@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
```