- 修复前端路由守卫:未登录时不显示提示,直接跳转登录页 - 修复API拦截器:401错误不显示提示,直接跳转 - 增强验证码显示:图片尺寸从120x40增加到200x80 - 增大验证码字体:从28号增加到48号 - 优化验证码字符:排除易混淆的0和1 - 减少干扰线:从5条减少到3条,添加背景色优化 - 增强登录API日志:添加详细的调试日志 - 增强验证码生成和验证日志 - 优化异常处理和错误追踪 影响文件: - src/router/index.ts - src/api/request.ts - app/services/auth_service.py - app/api/v1/auth.py - app/schemas/user.py 测试状态: - 前端构建通过 - 后端语法检查通过 - 验证码显示效果优化完成 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
425 lines
8.7 KiB
Markdown
425 lines
8.7 KiB
Markdown
# 文件管理模块快速开始指南
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 后端启动
|
||
|
||
#### 1. 数据库迁移
|
||
```bash
|
||
cd C:/Users/Administrator/asset_management_backend
|
||
|
||
# 激活虚拟环境
|
||
python -m venv venv
|
||
venv\Scripts\activate # Windows
|
||
# source venv/bin/activate # Linux/Mac
|
||
|
||
# 安装依赖
|
||
pip install -r requirements.txt
|
||
pip install python-multipart pillow
|
||
|
||
# 运行迁移
|
||
alembic upgrade head
|
||
```
|
||
|
||
#### 2. 创建上传目录
|
||
```bash
|
||
mkdir -p uploads/images
|
||
mkdir -p uploads/documents
|
||
mkdir -p uploads/thumbnails
|
||
mkdir -p uploads/temp
|
||
```
|
||
|
||
#### 3. 启动服务
|
||
```bash
|
||
python run.py
|
||
```
|
||
|
||
后端服务将运行在 `http://localhost:8000`
|
||
|
||
### 前端启动
|
||
|
||
#### 1. 安装依赖
|
||
```bash
|
||
cd C:/Users/Administrator/asset-management-frontend
|
||
|
||
npm install
|
||
```
|
||
|
||
#### 2. 启动开发服务器
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
前端服务将运行在 `http://localhost:5173`
|
||
|
||
## 📝 API测试示例
|
||
|
||
### 1. 文件上传(使用curl)
|
||
|
||
```bash
|
||
# 上传文件
|
||
curl -X POST http://localhost:8000/api/v1/files/upload \
|
||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||
-F "file=@/path/to/your/file.jpg" \
|
||
-F "remark=测试文件"
|
||
```
|
||
|
||
### 2. 获取文件列表
|
||
```bash
|
||
curl -X GET "http://localhost:8000/api/v1/files?page=1&page_size=20" \
|
||
-H "Authorization: Bearer YOUR_TOKEN"
|
||
```
|
||
|
||
### 3. 下载文件
|
||
```bash
|
||
curl -X GET http://localhost:8000/api/v1/files/1/download \
|
||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||
-o downloaded_file.jpg
|
||
```
|
||
|
||
### 4. 生成分享链接
|
||
```bash
|
||
curl -X POST http://localhost:8000/api/v1/files/1/share \
|
||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"expire_days": 7}'
|
||
```
|
||
|
||
## 💻 前端使用示例
|
||
|
||
### 1. 在页面中使用文件上传组件
|
||
|
||
```vue
|
||
<template>
|
||
<div>
|
||
<file-upload
|
||
:auto-upload="false"
|
||
:show-progress="true"
|
||
:show-image-preview="true"
|
||
@upload-success="handleUploadSuccess"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import FileUpload from '@/components/file/FileUpload.vue'
|
||
|
||
const handleUploadSuccess = (response, file) => {
|
||
ElMessage.success(`文件 ${file.name} 上传成功`)
|
||
console.log('上传响应:', response)
|
||
}
|
||
</script>
|
||
```
|
||
|
||
### 2. 在页面中使用文件列表组件
|
||
|
||
```vue
|
||
<template>
|
||
<div>
|
||
<file-list />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import FileList from '@/components/file/FileList.vue'
|
||
</script>
|
||
```
|
||
|
||
### 3. 使用文件API
|
||
|
||
```typescript
|
||
import { uploadFile, getFileList, downloadFile } from '@/api/file'
|
||
|
||
// 上传文件
|
||
const handleUpload = async (file: File) => {
|
||
try {
|
||
const response = await uploadFile(file, { remark: '测试' })
|
||
console.log('上传成功:', response)
|
||
} catch (error) {
|
||
console.error('上传失败:', error)
|
||
}
|
||
}
|
||
|
||
// 获取文件列表
|
||
const fetchFiles = async () => {
|
||
try {
|
||
const files = await getFileList({
|
||
page: 1,
|
||
page_size: 20,
|
||
keyword: 'test'
|
||
})
|
||
console.log('文件列表:', files)
|
||
} catch (error) {
|
||
console.error('获取失败:', error)
|
||
}
|
||
}
|
||
|
||
// 下载文件
|
||
const handleDownload = async (fileId: number, fileName: string) => {
|
||
try {
|
||
await downloadFile(fileId)
|
||
// 注意:实际下载需要在响应头处理
|
||
window.open(`http://localhost:8000/api/v1/files/${fileId}/download`)
|
||
} catch (error) {
|
||
console.error('下载失败:', error)
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🎯 常见功能实现
|
||
|
||
### 1. 批量上传
|
||
|
||
```vue
|
||
<template>
|
||
<file-upload
|
||
:multiple="true"
|
||
:limit="10"
|
||
:auto-upload="false"
|
||
ref="uploadRef"
|
||
>
|
||
<template #tip>
|
||
<div>最多可上传10个文件</div>
|
||
</template>
|
||
</file-upload>
|
||
|
||
<el-button @click="submitUpload">开始上传</el-button>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import FileUpload from '@/components/file/FileUpload.vue'
|
||
|
||
const uploadRef = ref()
|
||
|
||
const submitUpload = () => {
|
||
uploadRef.value.submitUpload()
|
||
}
|
||
</script>
|
||
```
|
||
|
||
### 2. 图片预览
|
||
|
||
```vue
|
||
<template>
|
||
<div>
|
||
<el-button @click="showPreview = true">预览图片</el-button>
|
||
|
||
<image-preview
|
||
v-model:visible="showPreview"
|
||
:images="images"
|
||
:initial-index="0"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import ImagePreview from '@/components/file/ImagePreview.vue'
|
||
|
||
const showPreview = ref(false)
|
||
const images = ref([
|
||
{
|
||
url: 'http://localhost:8000/api/v1/files/1/preview',
|
||
name: '图片1.jpg'
|
||
},
|
||
{
|
||
url: 'http://localhost:8000/api/v1/files/2/preview',
|
||
name: '图片2.jpg'
|
||
}
|
||
])
|
||
</script>
|
||
```
|
||
|
||
### 3. 文件分享
|
||
|
||
```typescript
|
||
import { createShareLink } from '@/api/file'
|
||
import { ElMessage } from 'element-plus'
|
||
|
||
const shareFile = async (fileId: number) => {
|
||
try {
|
||
const result = await createShareLink(fileId, 7) // 7天有效期
|
||
ElMessage.success(`分享链接: ${result.share_url}`)
|
||
|
||
// 复制到剪贴板
|
||
navigator.clipboard.writeText(result.share_url)
|
||
} catch (error) {
|
||
ElMessage.error('生成分享链接失败')
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 大文件分片上传
|
||
|
||
```typescript
|
||
import { initChunkUpload, uploadChunk, completeChunkUpload } from '@/api/file'
|
||
|
||
const uploadLargeFile = async (file: File) => {
|
||
const CHUNK_SIZE = 5 * 1024 * 1024 // 5MB每片
|
||
const totalChunks = Math.ceil(file.size / CHUNK_SIZE)
|
||
|
||
// 1. 初始化
|
||
const { upload_id } = await initChunkUpload({
|
||
file_name: file.name,
|
||
file_size: file.size,
|
||
file_type: file.type,
|
||
total_chunks: totalChunks
|
||
})
|
||
|
||
// 2. 上传分片
|
||
for (let i = 0; i < totalChunks; i++) {
|
||
const start = i * CHUNK_SIZE
|
||
const end = Math.min(start + CHUNK_SIZE, file.size)
|
||
const chunk = file.slice(start, end)
|
||
|
||
await uploadChunk(upload_id, i, chunk)
|
||
console.log(`分片 ${i + 1}/${totalChunks} 上传完成`)
|
||
}
|
||
|
||
// 3. 完成上传
|
||
const result = await completeChunkUpload({
|
||
upload_id: upload_id,
|
||
file_name: file.name
|
||
})
|
||
|
||
console.log('上传完成:', result)
|
||
}
|
||
```
|
||
|
||
## 🔍 API响应示例
|
||
|
||
### 上传文件响应
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"file_name": "a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg",
|
||
"original_name": "photo.jpg",
|
||
"file_size": 1024000,
|
||
"file_type": "image/jpeg",
|
||
"file_path": "uploads/2026/01/24/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg",
|
||
"download_url": "http://localhost:8000/api/v1/files/1/download",
|
||
"preview_url": "http://localhost:8000/api/v1/files/1/preview",
|
||
"message": "上传成功"
|
||
}
|
||
```
|
||
|
||
### 文件列表响应
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"file_name": "uuid.jpg",
|
||
"original_name": "photo.jpg",
|
||
"file_size": 1024000,
|
||
"file_type": "image/jpeg",
|
||
"file_ext": "jpg",
|
||
"uploader_id": 1,
|
||
"uploader_name": "张三",
|
||
"upload_time": "2026-01-24T10:30:00",
|
||
"thumbnail_path": "uploads/thumbnails/2026/01/24/thumb_uuid.jpg",
|
||
"download_count": 5,
|
||
"remark": null
|
||
}
|
||
]
|
||
```
|
||
|
||
### 分享链接响应
|
||
```json
|
||
{
|
||
"share_code": "AbCdEf1234567890",
|
||
"share_url": "http://localhost:8000/api/v1/files/share/AbCdEf1234567890",
|
||
"expire_time": "2026-01-31T10:30:00"
|
||
}
|
||
```
|
||
|
||
### 文件统计响应
|
||
```json
|
||
{
|
||
"total_files": 150,
|
||
"total_size": 524288000,
|
||
"total_size_human": "500.00 MB",
|
||
"type_distribution": {
|
||
"image/jpeg": 80,
|
||
"image/png": 40,
|
||
"application/pdf": 30
|
||
},
|
||
"upload_today": 10,
|
||
"upload_this_week": 50,
|
||
"upload_this_month": 120,
|
||
"top_uploaders": [
|
||
{ "uploader_id": 1, "count": 50 },
|
||
{ "uploader_id": 2, "count": 30 }
|
||
]
|
||
}
|
||
```
|
||
|
||
## 🛠️ 故障排除
|
||
|
||
### 问题1:上传文件失败
|
||
**错误信息**:`413 Request Entity Too Large`
|
||
|
||
**解决方案**:
|
||
检查Nginx配置(如果使用):
|
||
```nginx
|
||
client_max_body_size 100M;
|
||
```
|
||
|
||
### 问题2:文件类型不支持
|
||
**错误信息**:`不支持的文件类型`
|
||
|
||
**解决方案**:
|
||
在 `app/services/file_service.py` 中添加文件类型到白名单:
|
||
```python
|
||
ALLOWED_MIME_TYPES = {
|
||
'image/webp', # 添加新类型
|
||
# ... 其他类型
|
||
}
|
||
```
|
||
|
||
### 问题3:缩略图生成失败
|
||
**错误信息**:`生成缩略图失败`
|
||
|
||
**解决方案**:
|
||
确保安装了Pillow库:
|
||
```bash
|
||
pip install pillow
|
||
```
|
||
|
||
### 问题4:前端无法预览图片
|
||
**错误信息**:CORS错误
|
||
|
||
**解决方案**:
|
||
在后端添加CORS中间件:
|
||
```python
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=["http://localhost:5173"],
|
||
allow_credentials=True,
|
||
allow_methods=["*"],
|
||
allow_headers=["*"],
|
||
)
|
||
```
|
||
|
||
## 📚 更多资源
|
||
|
||
- [完整文档](./FILE_MANAGEMENT_README.md)
|
||
- [API文档](./API_QUICK_REFERENCE.md)
|
||
- [项目主文档](./README.md)
|
||
|
||
## 💡 提示
|
||
|
||
1. **开发环境**:使用小文件测试,避免上传大文件
|
||
2. **生产环境**:建议使用云存储服务(OSS、S3等)
|
||
3. **安全性**:生产环境务必配置文件类型和大小限制
|
||
4. **性能**:大文件使用分片上传,提高上传成功率
|
||
5. **监控**:记录文件上传、下载日志,便于问题追踪
|
||
|
||
---
|
||
|
||
如有问题,请查看完整文档或联系开发团队。
|