fastapi-file-management-service
fastapi-file-management-service
https://github.com/hanieas/fastapi-file-management-service
- Introduction
- Technology Stack and Features
- Why a Separate File Management Service?
- How to Use it?
- API Endpoints
- Contributing
This microservice is designed to manage all file-related tasks. It uses MinIO for object storage and MySQL for managing file metadata. We support chunk uploads for handling large files efficiently, with Celery running background tasks to ensure smooth performance.
- ⚡ FastAPI for the Python backend API.
- 🧰 SQLAlchemy for the Python SQL database interactions (ORM).
- 🔍 Pydantic, used by FastAPI, for the data validation and settings management.
- 🗄️ MYSQL as the SQL database.
- 🔄 Alembic for database migrations.
- 🔧 Celery with RabbitMQ for task queue management and background processing.
- 💾 MinIO for scalable object storage with chunk upload support.
- ✅ Pytest for testing to ensure code reliability and functionality.
- 🐋 Docker Compose for development and production.
- Centralizes file operations, making management and maintenance easier.
- Enables scaling file handling independently of other services.
- Simplifies updates and changes to file handling without impacting other parts of the system.
- Reduces code duplication by keeping file upload and retrieval logic in one place, resulting in cleaner code.
时序图解释
初始化上传会话
- 客户端向 FastAPI 发送
POST /api/v1/file/upload/init/
请求,开始新的文件上传会话。- FastAPI 在 MySQL 中插入上传会话的元数据,并返回会话 ID 给客户端。
分块上传
- 客户端循环发送
POST /api/v1/file/upload/chunk/
请求,将文件的每个分块上传到 FastAPI。- FastAPI 将接收到的分块保存到文件系统的指定目录下。
- FastAPI 更新 MySQL 数据库中的分块状态。
完成上传
- 客户端向 FastAPI 发送
POST /api/v1/file/upload/complete/
请求,告知上传完成。- FastAPI 在 MySQL 中标记上传为完成,并将文件合并任务加入到 Celery 任务队列。
- Celery 从任务队列中取出任务,将文件系统中的所有分块合并成一个完整的文件。
- Celery 将合并后的文件上传到 MinIO 对象存储服务。
- Celery 更新 MySQL 数据库中的文件处理状态。
- FastAPI 向客户端返回上传成功的消息。
这个更新后的时序图准确反映了文件分块上传过程中各个组件的交互,以及分块存储和合并的实际流程。
对应前端分块上传逻辑
代码说明:
-
初始化上传会话:
- 调用
/api/v1/file/upload/init/
接口,发送文件名和总分块数,获取会话 ID。
- 调用
-
分块上传:
- 计算每个分块的起始和结束位置,使用
File.slice
方法分割文件。 - 为每个分块创建一个
FormData
对象,包含会话 ID、分块索引和分块数据。 - 循环调用
/api/v1/file/upload/chunk/
接口上传每个分块。
- 计算每个分块的起始和结束位置,使用
-
完成上传:
- 所有分块上传完成后,调用
/api/v1/file/upload/complete/
接口,发送会话 ID 以完成上传。
- 所有分块上传完成后,调用
async function uploadFile(file) { const chunkSize = 1024 * 1024; // 1MB 每个分块大小 const totalChunks = Math.ceil(file.size / chunkSize); // 初始化上传会话 const initResponse = await fetch('/api/v1/file/upload/init/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileName: file.name, totalChunks: totalChunks }) }); const { sessionId } = await initResponse.json(); // 分块上传 for (let i = 0; i < totalChunks; i++) { const start = i * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('sessionId', sessionId); formData.append('chunkIndex', i); formData.append('chunk', chunk); await fetch('/api/v1/file/upload/chunk/', { method: 'POST', body: formData }); } // 完成上传 await fetch('/api/v1/file/upload/complete/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: sessionId }) }); console.log('文件上传完成'); } // 使用示例 const inputElement = document.getElementById('fileInput'); inputElement.addEventListener('change', async (event) => { const file = event.target.files[0]; if (file) { await uploadFile(file); } });
出处:http://www.cnblogs.com/lightsong/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。