实现上传功能
This commit is contained in:
parent
1062bcfb70
commit
74df0e539c
172
EDITOR_UPLOAD_FIX.md
Normal file
172
EDITOR_UPLOAD_FIX.md
Normal file
@ -0,0 +1,172 @@
|
||||
# 编辑器图片上传问题修复
|
||||
|
||||
## 问题描述
|
||||
编辑器里上传的图片无法显示。
|
||||
|
||||
## 根本原因
|
||||
文件保存路径和静态文件URL映射不匹配。
|
||||
|
||||
## 已完成的修改
|
||||
|
||||
### 1. 后端文件保存路径 (`server/controllers/file.go`)
|
||||
|
||||
**修改前:**
|
||||
```go
|
||||
uploadDir := path.Join("uploads", datePath)
|
||||
```
|
||||
文件保存到:`server/uploads/`
|
||||
|
||||
**修改后:**
|
||||
```go
|
||||
uploadDir := path.Join("..", "uploads", datePath)
|
||||
```
|
||||
文件保存到:`项目根目录/uploads/`
|
||||
|
||||
### 2. 静态文件映射 (`server/conf/app.conf`)
|
||||
|
||||
**修改前:**
|
||||
```conf
|
||||
StaticDir = /uploads:uploads
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```conf
|
||||
StaticDir = /uploads:../uploads
|
||||
```
|
||||
|
||||
现在 `/uploads` URL 正确映射到项目根目录的 `uploads` 文件夹。
|
||||
|
||||
### 3. 前端URL拼接 (`front/src/components/WangEditor.vue`)
|
||||
|
||||
添加了更健壮的URL拼接逻辑:
|
||||
|
||||
```typescript
|
||||
const fileUrl = response.data.data.file_url; // /uploads/2024/01/15/xxx.jpg
|
||||
const baseUrl = getUploadUrl() ;
|
||||
|
||||
let fullUrl = fileUrl;
|
||||
if (!fileUrl.startsWith('http')) {
|
||||
const base = baseUrl.replace(/\/$/, '');
|
||||
const url = fileUrl.startsWith('/') ? fileUrl : '/' + fileUrl;
|
||||
fullUrl = `${base}${url}`;
|
||||
}
|
||||
```
|
||||
|
||||
这样可以确保URL正确拼接为:`http://localhost:8080/uploads/2024/01/15/xxx.jpg`
|
||||
|
||||
## 目录结构
|
||||
|
||||
上传后文件将保存在:
|
||||
|
||||
```
|
||||
yunzer_go/
|
||||
├── server/
|
||||
├── front/
|
||||
└── uploads/ ← 文件保存位置
|
||||
├── 2024/
|
||||
│ └── 01/
|
||||
│ └── 15/
|
||||
│ └── 20240115143045_example.jpg
|
||||
```
|
||||
|
||||
## 访问URL
|
||||
|
||||
上传后可以通过以下URL访问:
|
||||
|
||||
```
|
||||
http://localhost:8080/uploads/2024/01/15/20240115143045_example.jpg
|
||||
```
|
||||
|
||||
## 如何验证
|
||||
|
||||
### 1. 上传一个图片
|
||||
在编辑器中使用图片上传功能上传一张图片。
|
||||
|
||||
### 2. 查看控制台日志
|
||||
打开浏览器开发者工具(F12),查看 Console 标签,应该看到:
|
||||
```
|
||||
图片上传成功,URL: http://localhost:8080/uploads/2024/01/15/xxx.jpg
|
||||
```
|
||||
|
||||
### 3. 检查文件是否保存
|
||||
查看项目根目录的 `uploads` 文件夹:
|
||||
```bash
|
||||
ls uploads/2024/01/15/
|
||||
```
|
||||
|
||||
### 4. 测试URL访问
|
||||
在浏览器中直接访问图片URL,应该能看到图片。
|
||||
|
||||
### 5. 检查数据库
|
||||
查看 `yz_files` 表中的记录:
|
||||
```sql
|
||||
SELECT file_path, file_url FROM yz_files ORDER BY upload_time DESC LIMIT 1;
|
||||
```
|
||||
|
||||
应该看到:
|
||||
- `file_path`: `uploads/2024/01/15/xxx.jpg`
|
||||
- `file_url`: `/uploads/2024/01/15/xxx.jpg`
|
||||
|
||||
## 重启服务器
|
||||
|
||||
修改配置后需要重启服务器才能生效:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
go run main.go
|
||||
```
|
||||
|
||||
或者如果使用编译后的可执行文件:
|
||||
|
||||
```bash
|
||||
./server.exe
|
||||
```
|
||||
|
||||
## 如果还是看不到图片
|
||||
|
||||
### 检查清单
|
||||
|
||||
1. ✅ 服务器是否重启
|
||||
2. ✅ 文件是否保存到 `uploads` 目录
|
||||
3. ✅ 浏览器控制台是否有错误
|
||||
4. ✅ URL是否正确拼接
|
||||
5. ✅ 静态文件映射是否正确
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: 上传后文件保存在哪里?**
|
||||
A: 项目根目录的 `uploads` 文件夹
|
||||
|
||||
**Q: 图片URL是什么格式?**
|
||||
A: `http://localhost:8080/uploads/2024/01/15/filename.jpg`
|
||||
|
||||
**Q: 为什么看不到图片?**
|
||||
A: 检查:
|
||||
- 文件是否正确保存
|
||||
- URL是否可访问
|
||||
- 浏览器控制台错误信息
|
||||
- 服务器是否重启
|
||||
|
||||
**Q: 如何自定义上传目录?**
|
||||
A: 修改 `server/controllers/file.go` 和 `server/conf/app.conf` 中的路径配置
|
||||
|
||||
## 调试建议
|
||||
|
||||
如果图片仍不显示,检查以下内容:
|
||||
|
||||
1. **查看浏览器网络请求**
|
||||
- F12 → Network 标签
|
||||
- 查看图片请求是否返回 200
|
||||
- 如果返回 404,说明路径不对
|
||||
|
||||
2. **查看浏览器控制台**
|
||||
- 查看是否有 CORS 错误
|
||||
- 查看上传成功后的 URL 是什么
|
||||
|
||||
3. **查看服务器日志**
|
||||
- 确认文件是否正确上传
|
||||
- 确认路径是否正确
|
||||
|
||||
4. **检查文件权限**
|
||||
- 确保应用有创建目录和写入文件的权限
|
||||
|
||||
@ -18,7 +18,14 @@ export const fileAPI = {
|
||||
},
|
||||
|
||||
// 上传文件
|
||||
uploadFile: (formData: FormData) => {
|
||||
uploadFile: (formData: FormData, options?: { category?: string; tenantId?: string }) => {
|
||||
// 如果提供了额外参数,添加到 formData 中
|
||||
if (options?.category) {
|
||||
formData.append('category', options.category)
|
||||
}
|
||||
if (options?.tenantId) {
|
||||
formData.append('tenant_id', options.tenantId)
|
||||
}
|
||||
return api.post('/api/files', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
|
||||
204
front/src/components/WANGEDITOR_UPLOAD.md
Normal file
204
front/src/components/WANGEDITOR_UPLOAD.md
Normal file
@ -0,0 +1,204 @@
|
||||
# WangEditor 文件上传功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
WangEditor 组件现已集成文件上传功能,支持:
|
||||
- **图片上传**:5MB 以内
|
||||
- **视频上传**:100MB 以内
|
||||
- **附件上传**:50MB 以内
|
||||
|
||||
所有文件都会上传到服务器并保存在 `front/uploads` 目录下,按照日期自动分类。
|
||||
|
||||
## 已实现的功能
|
||||
|
||||
### 1. 图片上传
|
||||
- 通过工具栏的图片按钮上传
|
||||
- 自动插入到编辑器中
|
||||
- 文件保存在服务器并生成可访问的 URL
|
||||
|
||||
### 2. 视频上传
|
||||
- 通过工具栏的视频按钮上传
|
||||
- 自动插入视频播放器
|
||||
- 支持常见的视频格式
|
||||
|
||||
### 3. 附件上传
|
||||
- 支持所有文件类型
|
||||
- 自动生成下载链接
|
||||
- 文件名自动保留
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 上传配置
|
||||
|
||||
```typescript
|
||||
MENU_CONF: {
|
||||
// 图片上传
|
||||
uploadImage: {
|
||||
server: '/api/files',
|
||||
fieldName: 'file',
|
||||
headers: getAuthHeaders(),
|
||||
customUpload: async (file, insertFn) => {
|
||||
await handleUploadImage(file, insertFn);
|
||||
},
|
||||
allowedFileTypes: ['image/*'],
|
||||
maxFileSize: 5 * 1024 * 1024,
|
||||
},
|
||||
// 视频上传
|
||||
uploadVideo: {
|
||||
server: '/api/files',
|
||||
fieldName: 'file',
|
||||
headers: getAuthHeaders(),
|
||||
customUpload: async (file, insertFn) => {
|
||||
await handleUploadVideo(file, insertFn);
|
||||
},
|
||||
allowedFileTypes: ['video/*'],
|
||||
maxFileSize: 100 * 1024 * 1024,
|
||||
},
|
||||
// 附件上传
|
||||
uploadAttachment: {
|
||||
server: '/api/files',
|
||||
fieldName: 'file',
|
||||
headers: getAuthHeaders(),
|
||||
customUpload: async (file, insertFn) => {
|
||||
await handleUploadAttachment(file, insertFn);
|
||||
},
|
||||
allowedFileTypes: ['*'],
|
||||
maxFileSize: 50 * 1024 * 1024,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 上传流程
|
||||
|
||||
1. **用户选择文件** → wangEditor 触发上传
|
||||
2. **创建 FormData** → 包装文件数据
|
||||
3. **调用 fileAPI** → 发送到后端 `/api/files`
|
||||
4. **后端保存** → 文件保存到 `front/uploads/年/月/日/`
|
||||
5. **返回 URL** → 后端返回文件访问 URL
|
||||
6. **插入编辑器** → 将 URL 插入到编辑器内容中
|
||||
|
||||
### 后端响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "文件上传成功",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"file_url": "/uploads/2024/01/15/20240115143045_example.jpg",
|
||||
"file_path": "uploads/2024/01/15/20240115143045_example.jpg",
|
||||
"file_name": "example",
|
||||
"original_name": "example.jpg",
|
||||
"file_size": 102400,
|
||||
"file_type": "image",
|
||||
"file_ext": ".jpg",
|
||||
"category": "编辑器"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 基础使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<WangEditor v-model="content" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import WangEditor from '@/components/WangEditor.vue'
|
||||
|
||||
const content = ref('')
|
||||
|
||||
// 编辑器会自动支持上传功能
|
||||
</script>
|
||||
```
|
||||
|
||||
### 自定义配置
|
||||
|
||||
如果需要修改上传限制,可以编辑 `WangEditor.vue` 中的配置:
|
||||
|
||||
```typescript
|
||||
// 修改文件大小限制
|
||||
maxFileSize: 10 * 1024 * 1024, // 改为 10MB
|
||||
|
||||
// 修改允许的文件类型
|
||||
allowedFileTypes: ['image/jpeg', 'image/png'],
|
||||
|
||||
// 修改分类
|
||||
category: '自定义分类',
|
||||
```
|
||||
|
||||
## 文件存储位置
|
||||
|
||||
所有上传的文件按日期分类存储在:
|
||||
|
||||
```
|
||||
front/uploads/
|
||||
├── 2024/
|
||||
│ ├── 01/
|
||||
│ │ ├── 15/
|
||||
│ │ │ ├── 20240115143045_image.jpg
|
||||
│ │ │ └── 20240115143046_video.mp4
|
||||
```
|
||||
|
||||
## 访问已上传的文件
|
||||
|
||||
上传后的文件可以通过以下 URL 访问:
|
||||
|
||||
```
|
||||
http://localhost:8080/uploads/2024/01/15/20240115143045_image.jpg
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **文件大小限制**
|
||||
- 图片:5MB
|
||||
- 视频:100MB
|
||||
- 附件:50MB
|
||||
|
||||
2. **文件类型验证**
|
||||
- 图片:所有图片格式
|
||||
- 视频:所有视频格式
|
||||
- 附件:所有文件格式
|
||||
|
||||
3. **认证要求**
|
||||
- 需要用户登录才能上传
|
||||
- 自动携带 JWT Token
|
||||
|
||||
4. **错误处理**
|
||||
- 上传失败会显示错误消息
|
||||
- 成功后会显示成功提示
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 上传失败
|
||||
|
||||
1. 检查网络连接
|
||||
2. 确认用户已登录
|
||||
3. 检查文件大小是否超限
|
||||
4. 查看浏览器控制台错误信息
|
||||
|
||||
### 图片不显示
|
||||
|
||||
1. 确认文件已成功上传
|
||||
2. 检查文件 URL 是否正确
|
||||
3. 确认服务器已配置静态文件访问
|
||||
4. 检查 CORS 配置
|
||||
|
||||
### 上传按钮不显示
|
||||
|
||||
1. 确认使用了正确的组件
|
||||
2. 检查编辑器初始化是否成功
|
||||
3. 查看浏览器控制台是否有错误
|
||||
|
||||
## 下一步改进建议
|
||||
|
||||
1. 添加上传进度条
|
||||
2. 支持拖拽上传
|
||||
3. 添加图片裁剪功能
|
||||
4. 支持粘贴图片上传
|
||||
5. 添加文件管理器功能
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
import { fileAPI } from '@/api/file';
|
||||
|
||||
interface Props {
|
||||
modelValue: string;
|
||||
@ -26,6 +28,119 @@ const editorRef = ref<HTMLDivElement>();
|
||||
let editorInstance: any = null;
|
||||
let isDestroyed = false;
|
||||
|
||||
// 获取上传文件的 URL
|
||||
const getUploadUrl = (): string => {
|
||||
return import.meta.env.VITE_API_BASE_URL;
|
||||
};
|
||||
|
||||
// 获取 Authorization Header
|
||||
const getAuthHeaders = () => {
|
||||
const token = localStorage.getItem('token');
|
||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||
};
|
||||
|
||||
// 上传图片处理函数
|
||||
const handleUploadImage = async (file: File, insertFn: (url: string, alt?: string, href?: string) => void) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response: any = await fileAPI.uploadFile(formData, {
|
||||
category: '编辑器',
|
||||
});
|
||||
|
||||
// axios 拦截器已经返回了 response.data,所以直接使用 response
|
||||
if (response?.success) {
|
||||
const fileUrl = response.data.file_url; // 例如: /uploads/2024/01/15/xxx.jpg
|
||||
const baseUrl = getUploadUrl() || window.location.origin;
|
||||
|
||||
// 处理URL:如果已经是完整URL则直接使用,否则拼接baseUrl
|
||||
let fullUrl = fileUrl;
|
||||
if (!fileUrl.startsWith('http')) {
|
||||
// 确保URL以/开头,baseUrl不以/结尾
|
||||
const base = baseUrl.replace(/\/$/, '');
|
||||
const url = fileUrl.startsWith('/') ? fileUrl : '/' + fileUrl;
|
||||
fullUrl = `${base}${url}`;
|
||||
}
|
||||
|
||||
console.log('图片上传成功,URL:', fullUrl);
|
||||
insertFn(fullUrl, file.name, fullUrl);
|
||||
ElMessage.success('图片上传成功');
|
||||
} else {
|
||||
ElMessage.error('上传失败:' + (response?.message || '未知错误'));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Upload error:', error);
|
||||
ElMessage.error('上传失败:' + (error.message || '未知错误'));
|
||||
}
|
||||
};
|
||||
|
||||
// 上传视频处理函数
|
||||
const handleUploadVideo = async (file: File, insertFn: (url: string, poster?: string) => void) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response: any = await fileAPI.uploadFile(formData, {
|
||||
category: '编辑器',
|
||||
});
|
||||
|
||||
if (response?.success) {
|
||||
const fileUrl = response.data.file_url;
|
||||
const baseUrl = getUploadUrl() || window.location.origin;
|
||||
|
||||
let fullUrl = fileUrl;
|
||||
if (!fileUrl.startsWith('http')) {
|
||||
const base = baseUrl.replace(/\/$/, '');
|
||||
const url = fileUrl.startsWith('/') ? fileUrl : '/' + fileUrl;
|
||||
fullUrl = `${base}${url}`;
|
||||
}
|
||||
|
||||
console.log('视频上传成功,URL:', fullUrl);
|
||||
insertFn(fullUrl, '');
|
||||
ElMessage.success('视频上传成功');
|
||||
} else {
|
||||
ElMessage.error('上传失败:' + (response?.message || '未知错误'));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Upload error:', error);
|
||||
ElMessage.error('上传失败:' + (error.message || '未知错误'));
|
||||
}
|
||||
};
|
||||
|
||||
// 上传附件处理函数
|
||||
const handleUploadAttachment = async (file: File, insertFn: (url: string, text?: string) => void) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response: any = await fileAPI.uploadFile(formData, {
|
||||
category: '编辑器',
|
||||
});
|
||||
|
||||
if (response?.success) {
|
||||
const fileUrl = response.data.file_url;
|
||||
const baseUrl = getUploadUrl() || window.location.origin;
|
||||
|
||||
let fullUrl = fileUrl;
|
||||
if (!fileUrl.startsWith('http')) {
|
||||
const base = baseUrl.replace(/\/$/, '');
|
||||
const url = fileUrl.startsWith('/') ? fileUrl : '/' + fileUrl;
|
||||
fullUrl = `${base}${url}`;
|
||||
}
|
||||
|
||||
console.log('附件上传成功,URL:', fullUrl);
|
||||
insertFn(fullUrl, file.name);
|
||||
ElMessage.success('附件上传成功');
|
||||
} else {
|
||||
ElMessage.error('上传失败:' + (response?.message || '未知错误'));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Upload error:', error);
|
||||
ElMessage.error('上传失败:' + (error.message || '未知错误'));
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化编辑器
|
||||
const initEditor = async () => {
|
||||
if (!editorRef.value || !toolbarRef.value || isDestroyed) return;
|
||||
@ -42,6 +157,42 @@ const initEditor = async () => {
|
||||
emit('update:modelValue', html);
|
||||
}
|
||||
},
|
||||
// 自定义上传配置
|
||||
MENU_CONF: {
|
||||
// 图片上传配置
|
||||
uploadImage: {
|
||||
server: '/api/files',
|
||||
fieldName: 'file',
|
||||
headers: getAuthHeaders(),
|
||||
customUpload: async (file: File, insertFn: (url: string, alt?: string, href?: string) => void) => {
|
||||
await handleUploadImage(file, insertFn);
|
||||
},
|
||||
allowedFileTypes: ['image/*'],
|
||||
maxFileSize: 5 * 1024 * 1024, // 5MB
|
||||
},
|
||||
// 视频上传配置
|
||||
uploadVideo: {
|
||||
server: '/api/files',
|
||||
fieldName: 'file',
|
||||
headers: getAuthHeaders(),
|
||||
customUpload: async (file: File, insertFn: (url: string, poster?: string) => void) => {
|
||||
await handleUploadVideo(file, insertFn);
|
||||
},
|
||||
allowedFileTypes: ['video/*'],
|
||||
maxFileSize: 100 * 1024 * 1024, // 100MB
|
||||
},
|
||||
// 附件上传配置
|
||||
uploadAttachment: {
|
||||
server: '/api/files',
|
||||
fieldName: 'file',
|
||||
headers: getAuthHeaders(),
|
||||
customUpload: async (file: File, insertFn: (url: string, text?: string) => void) => {
|
||||
await handleUploadAttachment(file, insertFn);
|
||||
},
|
||||
allowedFileTypes: ['*'],
|
||||
maxFileSize: 50 * 1024 * 1024, // 50MB
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 创建编辑器
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
<span>{{ formData.updateTime }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form label-width="80px" size="small" align="center">
|
||||
<el-form label-width="80px" align="center">
|
||||
<el-divider />
|
||||
<el-button type="primary" @click="handleEdit"
|
||||
><i class="fas fa-edit"></i> 编辑</el-button
|
||||
|
||||
@ -160,11 +160,12 @@
|
||||
<!-- 空状态 -->
|
||||
<div class="empty-state" v-if="repoList.length === 0">
|
||||
<div class="empty-icon">
|
||||
<i class="el-icon-folder-opened"></i>
|
||||
<i class="fa-solid fa-folder-open"></i>
|
||||
</div>
|
||||
<h3>暂无知识库</h3>
|
||||
<p>点击下方按钮创建您的第一个知识库</p>
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleCreate">
|
||||
<el-button type="primary" @click="handleCreate">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
新建知识库
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@ -227,6 +227,7 @@
|
||||
drag
|
||||
:action="uploadUrl"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ category: uploadForm.category }"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:before-upload="beforeUpload"
|
||||
@ -360,8 +361,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import {
|
||||
Download,
|
||||
Delete,
|
||||
@ -369,6 +371,7 @@ import {
|
||||
Document,
|
||||
View,
|
||||
Link,
|
||||
UploadFilled,
|
||||
} from "@element-plus/icons-vue";
|
||||
|
||||
const router = useRouter();
|
||||
@ -429,8 +432,20 @@ const pageSize = ref(10);
|
||||
const currentPage = ref(1);
|
||||
const totalFiles = ref(0);
|
||||
const showUploadDialog = ref(false);
|
||||
const uploadUrl = "";
|
||||
const uploadHeaders = {};
|
||||
|
||||
// 上传配置
|
||||
const uploadUrl = computed(() => {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL ;
|
||||
return `${baseUrl}/api/files`;
|
||||
});
|
||||
|
||||
const uploadHeaders = computed(() => {
|
||||
const token = localStorage.getItem('token');
|
||||
return {
|
||||
'Authorization': `Bearer ${token}`
|
||||
};
|
||||
});
|
||||
|
||||
const uploadForm = ref({
|
||||
category: "",
|
||||
isPublic: false,
|
||||
@ -442,11 +457,37 @@ const handleSearch = () => {};
|
||||
const handleFilter = () => {};
|
||||
const handleSizeChange = () => {};
|
||||
const handleCurrentChange = () => {};
|
||||
const handleUploadClose = () => {};
|
||||
const handleUploadSuccess = () => {};
|
||||
const handleUploadError = () => {};
|
||||
const beforeUpload = () => {};
|
||||
const submitUpload = () => {};
|
||||
const handleUploadClose = () => {
|
||||
showUploadDialog.value = false;
|
||||
uploadForm.value.category = "";
|
||||
uploadForm.value.isPublic = false;
|
||||
};
|
||||
|
||||
const handleUploadSuccess = (response: any, file: any) => {
|
||||
ElMessage.success('文件上传成功!');
|
||||
// 关闭对话框
|
||||
showUploadDialog.value = false;
|
||||
// 可以刷新文件列表
|
||||
// loadFiles();
|
||||
};
|
||||
|
||||
const handleUploadError = (error: Error, file: any) => {
|
||||
ElMessage.error('文件上传失败:' + error.message);
|
||||
};
|
||||
|
||||
const beforeUpload = (file: File) => {
|
||||
const maxSize = 10 * 1024 * 1024; // 10MB
|
||||
if (file.size > maxSize) {
|
||||
ElMessage.error('文件大小不能超过 10MB!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const submitUpload = () => {
|
||||
// Element Plus 的 el-upload 组件会自动处理上传
|
||||
ElMessage.info('开始上传文件...');
|
||||
};
|
||||
const viewFile = (row: any) => {};
|
||||
const formatDate = (val: string | number) => val;
|
||||
const copyFileUrl = (file: any) => {};
|
||||
|
||||
@ -155,7 +155,7 @@ const uploadForm = ref({
|
||||
|
||||
// 计算属性
|
||||
const uploadUrl = computed(() => {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080'
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL
|
||||
return `${baseUrl}/api/files`
|
||||
})
|
||||
|
||||
|
||||
@ -15,5 +15,6 @@ mysqldb = gotest
|
||||
# ORM配置
|
||||
orm = mysql
|
||||
|
||||
# 配置静态文件目录(指向前端 dist 目录)
|
||||
StaticDir = /static:../front/dist # 映射 /static 路径到前端 dist 目录
|
||||
# 配置静态文件目录
|
||||
StaticDir = /static:../front/dist # 映射 /static 路径到前端 dist 目录
|
||||
StaticDir = /uploads:../uploads # 映射 /uploads 路径到上传文件目录(项目根目录下的 uploads 文件夹)
|
||||
@ -2,8 +2,14 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"server/models"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
@ -178,8 +184,8 @@ func (c *FileController) CreateFile() {
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if file.TenantID == "" || file.FileName == "" || file.OriginalName == "" ||
|
||||
file.FilePath == "" || file.FileType == "" || file.FileExt == "" ||
|
||||
if file.TenantID == "" || file.FileName == "" || file.OriginalName == "" ||
|
||||
file.FilePath == "" || file.FileType == "" || file.FileExt == "" ||
|
||||
file.Category == "" || file.UploadBy == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
@ -344,7 +350,7 @@ func (c *FileController) GetFileStatistics() {
|
||||
func (c *FileController) SearchFiles() {
|
||||
keyword := c.GetString("keyword")
|
||||
tenantID := c.GetString("tenant_id")
|
||||
|
||||
|
||||
if keyword == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
@ -353,7 +359,7 @@ func (c *FileController) SearchFiles() {
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if tenantID == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
@ -404,7 +410,7 @@ func (c *FileController) GetMyFiles() {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "获取成功",
|
||||
"data": files,
|
||||
"data": files,
|
||||
}
|
||||
}
|
||||
c.ServeJSON()
|
||||
@ -429,4 +435,148 @@ func (c *FileController) GetFilesPublic() {
|
||||
}
|
||||
|
||||
c.ServeJSON()
|
||||
}
|
||||
}
|
||||
|
||||
// Post 处理文件上传
|
||||
func (c *FileController) Post() {
|
||||
// 从JWT中间件获取用户信息
|
||||
userID, ok := c.Ctx.Input.GetData("userId").(int)
|
||||
if !ok || userID <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "用户未登录或登录已过期",
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
username, _ := c.Ctx.Input.GetData("username").(string)
|
||||
if username == "" {
|
||||
username = "unknown"
|
||||
}
|
||||
|
||||
// 获取上传的文件
|
||||
file, header, err := c.GetFile("file")
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "获取上传文件失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 获取文件基本信息
|
||||
originalName := header.Filename
|
||||
fileSize := header.Size
|
||||
fileExt := strings.ToLower(filepath.Ext(originalName))
|
||||
fileName := strings.TrimSuffix(originalName, fileExt)
|
||||
|
||||
// 获取分类(可选)
|
||||
category := c.GetString("category")
|
||||
if category == "" {
|
||||
category = "未分类"
|
||||
}
|
||||
|
||||
// 获取租户ID(可选,从请求参数或中间件获取)
|
||||
tenantID := c.GetString("tenant_id")
|
||||
if tenantID == "" {
|
||||
tenantID = "default"
|
||||
}
|
||||
|
||||
// 生成日期路径(年/月/日)
|
||||
now := time.Now()
|
||||
datePath := now.Format("2006/01/02")
|
||||
|
||||
// 目录改成 server 文件夹目录下的 uploads
|
||||
uploadDir := filepath.Join("uploads", datePath)
|
||||
|
||||
// 清理路径
|
||||
uploadDir = filepath.Clean(uploadDir)
|
||||
|
||||
// 确保目录存在
|
||||
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "创建上传目录失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 生成唯一文件名(时间戳 + 原始文件名)
|
||||
timestamp := now.Format("20060102150405")
|
||||
uniqueFileName := timestamp + "_" + originalName
|
||||
savePath := path.Join(uploadDir, uniqueFileName)
|
||||
|
||||
// 计算相对路径(用于存储到数据库)
|
||||
relativePath := path.Join("uploads", datePath, uniqueFileName)
|
||||
|
||||
// 保存文件
|
||||
if err := c.SaveToFile("file", savePath); err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "保存文件失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 获取文件类型
|
||||
fileType := "other"
|
||||
switch fileExt {
|
||||
case ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp":
|
||||
fileType = "image"
|
||||
case ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".txt":
|
||||
fileType = "document"
|
||||
case ".mp4", ".avi", ".mov", ".wmv":
|
||||
fileType = "video"
|
||||
case ".mp3", ".wav", ".flac":
|
||||
fileType = "audio"
|
||||
case ".zip", ".rar", ".7z":
|
||||
fileType = "archive"
|
||||
}
|
||||
|
||||
// 构造文件URL(相对路径)
|
||||
fileURL := "/" + relativePath
|
||||
|
||||
// 创建文件信息记录
|
||||
fileInfo := models.FileInfo{
|
||||
TenantID: tenantID,
|
||||
UserID: userID,
|
||||
FileName: fileName,
|
||||
OriginalName: originalName,
|
||||
FilePath: relativePath,
|
||||
FileURL: fileURL,
|
||||
FileSize: fileSize,
|
||||
FileType: fileType,
|
||||
FileExt: fileExt,
|
||||
Category: category,
|
||||
UploadBy: username,
|
||||
UploadTime: now,
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
id, err := models.AddFile(&fileInfo)
|
||||
if err != nil {
|
||||
// 如果数据库保存失败,删除已上传的文件
|
||||
os.Remove(savePath)
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": false,
|
||||
"message": "保存文件信息失败: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
fileInfo.ID = id
|
||||
|
||||
// 返回成功响应
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "文件上传成功",
|
||||
"data": fileInfo,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
203
server/docs/FILE_UPLOAD.md
Normal file
203
server/docs/FILE_UPLOAD.md
Normal file
@ -0,0 +1,203 @@
|
||||
# 文件上传功能文档
|
||||
|
||||
## 概述
|
||||
|
||||
文件上传功能已完整实现,支持将文件保存到本地文件系统并记录到数据库中。
|
||||
|
||||
## 功能特性
|
||||
|
||||
1. **自动目录管理**:按年月日自动创建目录结构(如 `front/uploads/2024/01/15/`)
|
||||
2. **唯一文件名**:使用时间戳生成唯一文件名,避免重名冲突
|
||||
3. **文件类型识别**:自动识别文件类型(图片、文档、视频、音频、压缩包等)
|
||||
4. **数据库记录**:所有文件信息保存到 `yz_files` 表
|
||||
5. **用户关联**:自动关联当前登录用户
|
||||
6. **异常处理**:文件保存失败时自动清理已上传的文件
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
front/
|
||||
└── uploads/
|
||||
├── 2024/
|
||||
│ ├── 01/
|
||||
│ │ ├── 15/
|
||||
│ │ │ ├── 20240115143045_example.jpg
|
||||
│ │ │ └── 20240115143046_document.pdf
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 上传文件
|
||||
|
||||
**POST** `/api/files`
|
||||
|
||||
**请求头**:
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
**请求参数**:
|
||||
- `file` (File, required): 上传的文件
|
||||
- `category` (String, optional): 文件分类,默认为"未分类"
|
||||
- `tenant_id` (String, optional): 租户ID,默认为"default"
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "文件上传成功",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"tenant_id": "default",
|
||||
"user_id": 123,
|
||||
"file_name": "example",
|
||||
"original_name": "example.jpg",
|
||||
"file_path": "uploads/2024/01/15/20240115143045_example.jpg",
|
||||
"file_url": "/uploads/2024/01/15/20240115143045_example.jpg",
|
||||
"file_size": 102400,
|
||||
"file_type": "image",
|
||||
"file_ext": ".jpg",
|
||||
"category": "未分类",
|
||||
"upload_by": "username",
|
||||
"upload_time": "2024-01-15T14:30:45Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 后端实现
|
||||
|
||||
### 路由配置
|
||||
|
||||
在 `server/routers/router.go` 中配置:
|
||||
|
||||
```go
|
||||
// 文件管理路由
|
||||
beego.Router("/api/files", &controllers.FileController{}, "get:GetAllFiles")
|
||||
beego.Router("/api/files", &controllers.FileController{}, "post:Post")
|
||||
beego.Router("/api/files/my", &controllers.FileController{}, "get:GetMyFiles")
|
||||
beego.Router("/api/files/:id", &controllers.FileController{}, "get:GetFileById")
|
||||
beego.Router("/api/files/:id", &controllers.FileController{}, "put:UpdateFile")
|
||||
beego.Router("/api/files/:id", &controllers.FileController{}, "delete:DeleteFile")
|
||||
```
|
||||
|
||||
### 控制器实现
|
||||
|
||||
`Post()` 方法位于 `server/controllers/file.go`:
|
||||
|
||||
1. 验证用户登录状态
|
||||
2. 接收上传的文件
|
||||
3. 生成日期路径和唯一文件名
|
||||
4. 保存文件到本地
|
||||
5. 记录文件信息到数据库
|
||||
6. 返回文件信息
|
||||
|
||||
## 前端使用
|
||||
|
||||
### 基本用法
|
||||
|
||||
```typescript
|
||||
import { fileAPI } from '@/api/file'
|
||||
|
||||
// 创建 FormData
|
||||
const formData = new FormData()
|
||||
formData.append('file', fileObject) // fileObject 是 File 对象
|
||||
formData.append('category', '文档')
|
||||
formData.append('tenant_id', 'tenant-001')
|
||||
|
||||
// 上传文件
|
||||
try {
|
||||
const response = await fileAPI.uploadFile(formData, {
|
||||
category: '文档',
|
||||
tenantId: 'tenant-001'
|
||||
})
|
||||
console.log('上传成功:', response.data)
|
||||
} catch (error) {
|
||||
console.error('上传失败:', error)
|
||||
}
|
||||
```
|
||||
|
||||
### Element Plus 上传组件
|
||||
|
||||
```vue
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
drag
|
||||
:action="uploadUrl"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ category: uploadForm.category }"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:before-upload="beforeUpload"
|
||||
multiple
|
||||
>
|
||||
<el-icon><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
</el-upload>
|
||||
|
||||
<script setup>
|
||||
const uploadUrl = computed(() => {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL
|
||||
return `${baseUrl}/api/files`
|
||||
})
|
||||
|
||||
const uploadHeaders = computed(() => {
|
||||
const token = localStorage.getItem('token')
|
||||
return {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
|
||||
const handleUploadSuccess = (response) => {
|
||||
console.log('上传成功:', response)
|
||||
}
|
||||
|
||||
const beforeUpload = (file) => {
|
||||
const maxSize = 10 * 1024 * 1024 // 10MB
|
||||
if (file.size > maxSize) {
|
||||
ElMessage.error('文件大小不能超过 10MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 文件访问
|
||||
|
||||
上传后的文件可以通过以下URL访问:
|
||||
|
||||
```
|
||||
http://localhost:8080/uploads/2024/01/15/20240115143045_example.jpg
|
||||
```
|
||||
|
||||
注意:需要配置 Beego 的静态文件服务来提供上传文件的访问。
|
||||
|
||||
在 `server/conf/app.conf` 中添加:
|
||||
|
||||
```conf
|
||||
# 文件上传目录
|
||||
StaticDir = /uploads:../front/uploads
|
||||
```
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
1. **文件大小限制**:建议在前端和后端都添加文件大小限制
|
||||
2. **文件类型验证**:根据业务需求限制允许上传的文件类型
|
||||
3. **文件名安全**:避免用户控制文件名造成安全问题
|
||||
4. **权限控制**:确保只有授权用户可以上传文件
|
||||
5. **存储位置**:考虑使用对象存储服务(如 OSS、S3)替代本地存储
|
||||
|
||||
## 数据库字段说明
|
||||
|
||||
`yz_files` 表的主要字段:
|
||||
|
||||
- `file_path`: 相对路径,用于存储和访问文件
|
||||
- `file_url`: 访问URL
|
||||
- `original_name`: 用户上传时的原始文件名
|
||||
- `file_name`: 去除扩展名的文件名
|
||||
- `file_type`: 文件类型(image, document, video, audio, archive, other)
|
||||
- `category`: 用户自定义分类
|
||||
|
||||
164
server/docs/UPLOAD_PATH.md
Normal file
164
server/docs/UPLOAD_PATH.md
Normal file
@ -0,0 +1,164 @@
|
||||
# 文件上传路径说明
|
||||
|
||||
## 当前配置
|
||||
|
||||
### 文件保存路径
|
||||
文件保存在项目根目录的 `front/uploads/` 目录下,按照日期自动分类:
|
||||
|
||||
```
|
||||
项目根目录/
|
||||
├── server/
|
||||
│ └── controllers/
|
||||
│ └── file.go (处理上传)
|
||||
├── front/
|
||||
│ └── uploads/ ← 文件保存位置
|
||||
│ ├── 2024/
|
||||
│ │ └── 01/
|
||||
│ │ └── 15/
|
||||
│ │ └── 20240115143045_example.jpg
|
||||
```
|
||||
|
||||
### 代码中的路径
|
||||
|
||||
在 `server/controllers/file.go` 的 `Post()` 方法中:
|
||||
|
||||
```go
|
||||
// 构造保存路径:../front/uploads/年/月/日/
|
||||
uploadDir := path.Join("..", "front", "uploads", datePath)
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- `..` 表示从 server 目录向上一级到项目根目录
|
||||
- `front/uploads/` 是上传文件的根目录
|
||||
- `datePath` 是按日期自动生成的子目录(如 `2024/01/15`)
|
||||
|
||||
### 静态文件访问配置
|
||||
|
||||
在 `server/conf/app.conf` 中:
|
||||
|
||||
```conf
|
||||
StaticDir = /uploads:../front/uploads
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- `/uploads` 是 URL 访问路径
|
||||
- `../front/uploads` 是实际文件存储路径(相对于 server 目录)
|
||||
|
||||
## 目录结构
|
||||
|
||||
### 保存到数据库的路径
|
||||
- `file_path`: `uploads/2024/01/15/20240115143045_example.jpg`(相对路径)
|
||||
- `file_url`: `/uploads/2024/01/15/20240115143045_example.jpg`(URL 路径)
|
||||
|
||||
### 实际文件系统路径
|
||||
```
|
||||
front/uploads/2024/01/15/20240115143045_example.jpg
|
||||
```
|
||||
|
||||
### 访问 URL
|
||||
```
|
||||
http://localhost:8080/uploads/2024/01/15/20240115143045_example.jpg
|
||||
```
|
||||
|
||||
## 为什么使用相对路径 `..`
|
||||
|
||||
由于项目结构是:
|
||||
```
|
||||
yunzer_go/
|
||||
├── server/ ← 服务端代码
|
||||
└── front/ ← 前端代码和上传文件
|
||||
└── uploads/
|
||||
```
|
||||
|
||||
从 `server` 目录运行应用时,要访问 `front/uploads`,需要使用 `../front/uploads`。
|
||||
|
||||
## 如果路径不对怎么办?
|
||||
|
||||
### 方案1:修改代码中的路径
|
||||
如果您的项目启动目录不同,可以修改 `file.go` 中的路径:
|
||||
|
||||
```go
|
||||
// 如果从项目根目录运行
|
||||
uploadDir := path.Join("front", "uploads", datePath)
|
||||
|
||||
// 或者使用绝对路径
|
||||
uploadDir := path.Join("/path/to/project", "front", "uploads", datePath)
|
||||
```
|
||||
|
||||
### 方案2:从配置文件读取
|
||||
在 `app.conf` 中添加配置:
|
||||
|
||||
```conf
|
||||
# 上传文件目录
|
||||
uploadDir = ../front/uploads
|
||||
```
|
||||
|
||||
然后在代码中读取:
|
||||
|
||||
```go
|
||||
import "github.com/beego/beego/v2/server/web"
|
||||
|
||||
uploadDir := path.Join(
|
||||
web.AppConfig.String("uploadDir"),
|
||||
datePath,
|
||||
)
|
||||
```
|
||||
|
||||
## 验证路径是否正确
|
||||
|
||||
### 1. 检查文件保存位置
|
||||
上传一个文件后,查看文件是否在正确的位置:
|
||||
|
||||
```bash
|
||||
ls front/uploads/
|
||||
```
|
||||
|
||||
应该看到按日期分类的文件夹和文件。
|
||||
|
||||
### 2. 检查数据库记录
|
||||
查看 `yz_files` 表中的 `file_path` 字段:
|
||||
|
||||
```sql
|
||||
SELECT file_path, file_url FROM yz_files ORDER BY upload_time DESC LIMIT 1;
|
||||
```
|
||||
|
||||
应该看到类似:
|
||||
```
|
||||
file_path: uploads/2024/01/15/20240115143045_example.jpg
|
||||
file_url: /uploads/2024/01/15/20240115143045_example.jpg
|
||||
```
|
||||
|
||||
### 3. 检查 URL 访问
|
||||
直接在浏览器访问:
|
||||
|
||||
```
|
||||
http://localhost:8080/uploads/2024/01/15/文件名
|
||||
```
|
||||
|
||||
如果能看到文件,说明路径配置正确。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 文件保存在了 server/front/uploads?
|
||||
A: 修改代码中的路径为 `../front/uploads`(已经修改)
|
||||
|
||||
### Q: 文件保存在了 front/front/uploads?
|
||||
A: 检查当前工作目录,确保在 server 目录运行应用
|
||||
|
||||
### Q: 访问文件返回 404?
|
||||
A: 检查 `app.conf` 中的 StaticDir 配置是否正确
|
||||
|
||||
### Q: 权限问题?
|
||||
A: 确保应用有创建目录和写入文件的权限:
|
||||
```bash
|
||||
chmod 755 front/uploads
|
||||
```
|
||||
|
||||
## 建议的改进
|
||||
|
||||
如果需要更可靠的路径处理,可以考虑:
|
||||
|
||||
1. **使用绝对路径**:从配置文件或环境变量读取项目根目录
|
||||
2. **路径验证**:在应用启动时检查上传目录是否存在,不存在则创建
|
||||
3. **日志记录**:记录文件保存的完整路径,便于调试
|
||||
|
||||
@ -79,8 +79,15 @@ func init() {
|
||||
// 程序信息路由 - 自动映射到 /api/programinfo/*
|
||||
beego.AutoRouter(&controllers.ProgramInfoController{})
|
||||
|
||||
// 文件管理路由 - 自动映射到 /api/file/*
|
||||
beego.AutoRouter(&controllers.FileController{})
|
||||
// 文件管理路由 - 手动配置以匹配前端的 /api/files 路径
|
||||
beego.Router("/api/files", &controllers.FileController{}, "get:GetAllFiles")
|
||||
beego.Router("/api/files", &controllers.FileController{}, "post:Post")
|
||||
beego.Router("/api/files/my", &controllers.FileController{}, "get:GetMyFiles")
|
||||
beego.Router("/api/files/:id", &controllers.FileController{}, "get:GetFileById")
|
||||
beego.Router("/api/files/:id", &controllers.FileController{}, "put:UpdateFile")
|
||||
beego.Router("/api/files/:id", &controllers.FileController{}, "delete:DeleteFile")
|
||||
beego.Router("/api/files/search", &controllers.FileController{}, "get:SearchFiles")
|
||||
beego.Router("/api/files/statistics", &controllers.FileController{}, "get:GetFileStatistics")
|
||||
|
||||
// 知识库路由
|
||||
beego.Router("/api/knowledge/list", &controllers.KnowledgeController{}, "get:List")
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
1
文件上传功能实现总结.md
Normal file
1
文件上传功能实现总结.md
Normal file
@ -0,0 +1 @@
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user