Files
blog-press/docs/Web-Backend/Others/RustFS.md
2025-12-15 23:25:25 +08:00

215 lines
5.6 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.

# 简介
  [RustFS](https://rustfs.com.cn/) 是一个基于 Rust 构建的高性能分布式对象存储系统。
  具体以下特点:
- 高性能:基于 Rust 构建,确保极致的速度和资源效率。
- 分布式架构:可扩展且容错的设计,适用于大规模部署。
- S3 兼容性:与现有的 S3 兼容应用和工具无缝集成。
- 数据湖支持:专为高吞吐量的大数据和 AI 工作负载优化。
- 完全开源:采用 Apache 2.0 许可证,鼓励社区贡献和商业使用。
- 简单易用:设计简洁,易于部署和管理。
  S3 标准通常指 Amazon S3 的 API 接口规范,现已成为对象存储领域事实上的行业标准接口协议。它定义了对象存储的核心操作方式和数据模型。
- 对象:基本存储单元,包含数据、元数据和唯一标识符
- 桶:对象的逻辑容器,类似文件夹
- 扁平结构无传统目录层级通过键名Key定位
# 安装部署
  通过docker部署
```yml
services:
rustfs:
image: rustfs/rustfs:1.0.0-alpha.65
container_name: rustfs
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./data:/data
- ./logs:/logs
environment:
- TZ=Asia/Shanghai
- RUSTFS_ACCESS_KEY=admin
- RUSTFS_SECRET_KEY=19940822Cxx
- RUSTFS_CONSOLE_ENABLE=true
restart: unless-stopped
```
  其中9000是API端口9001是控制台端口。
  默认的登录账号密码为rustfsadmin / rustfsadmin。
# API调用
  登录控制台获取access_key和secret_access。
## Python
1. 安装AWS官方SDK依赖库
```cmd
pip install boto3
```
2. 配置与连接
```python
import boto3
from botocore.client import Config
from config.setting import settings
url = 'ip:port'
access_key = ''
secret_access = ''
s3 = boto3.client('s3',
endpoint_url=url,
aws_access_key_id=access_key,
aws_secret_access_key=secret_access,
config=Config(signature_version='s3v4'),
region_name='cn-east-1'
)
```
3. 上传文件
```python
async def upload_file(md5: str, file: UploadFile = File(...)) -> str:
file_ext = file.filename.split('.')[-1]
unique_filename = f"{md5}.{file_ext}"
file_content = await file.read()
# 上传到S3
s3.put_object(
Bucket=BUCKET,
Key=unique_filename,
Body=file_content,
ContentType=file.content_type
)
# 返回文件url
return f"{BUCKET}/{unique_filename}"
```
## Flutter
1. 安装依赖包
```yaml
dependencies:
minio: ^3.5.8
file_picker: ^10.3.3
```
2. 工具类
```dart
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:minio/io.dart';
import 'package:minio/minio.dart';
import 'file_utils.dart';
class MinIOHelper {
static final MinIOHelper _instance = MinIOHelper._internal();
factory MinIOHelper() => _instance;
final String ip = 'ip';
final int port = 9100;
final String fileUrl = 'http://ip:port';
final String accessKey = '';
final String secretKey = '';
MinIOHelper._internal() {
_minio = Minio(
endPoint: ip,
port: port,
accessKey: accessKey,
secretKey: secretKey,
useSSL: false,
);
}
late Minio _minio;
Future<String> uploadFile({
required PlatformFile file,
required String bucketName,
Function(double)? onProgress,
}) async {
try {
if (isImageFile(file.name)) {
// 压缩图片
final compressedFile = await compressImage(File(file.path!));
String hashName = await generateMD5HashName(compressedFile.path);
String fileName = '$hashName${getFileExtension(file.name)}';
await _minio.fPutObject(bucketName, fileName, compressedFile.path);
await compressedFile.delete();
return fileName;
} else {
String hashName = await generateMD5HashName(file.path!);
String fileName = '$hashName${getFileExtension(file.name)}';
await _minio.fPutObject(bucketName, fileName, file.path!);
return fileName;
}
} catch (e) {
throw Exception('文件上传失败: $e');
}
}
}
```
&emsp;&emsp;文件工具类:
```dart
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'log_utils.dart';
String getFileExtension(String fileName) {
if (fileName.contains('.')) {
return '.${fileName.split('.').last.toLowerCase()}';
}
return '';
}
Future<String> generateMD5HashName(String filePath) async {
final file = File(filePath);
final bytes = await file.readAsBytes();
final hash = md5.convert(bytes);
return hash.toString();
}
// 图片压缩方法
Future<File> compressImage(File file) async {
try {
// 获取压缩后的文件路径
final result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
'${file.parent.path}/compressed_${DateTime.now().millisecondsSinceEpoch}.jpg',
minWidth: 800,
minHeight: 600,
quality: 70,
format: CompressFormat.jpeg,
);
if (result == null) {
throw Exception('图片压缩失败');
}
return File(result.path);
} catch (e) {
logger.e('图片压缩失败,使用原文件: $e');
return file;
}
}
// 检查是否为图片文件
bool isImageFile(String fileName) {
final imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
final extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
return imageExtensions.contains(extension);
}
```