# 简介   [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 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'); } } } ```   文件工具类: ```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 generateMD5HashName(String filePath) async { final file = File(filePath); final bytes = await file.readAsBytes(); final hash = md5.convert(bytes); return hash.toString(); } // 图片压缩方法 Future 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); } ```