一、需求场景
为了实现系统中大量文件的可视化管理以及处理大文件在上传下载过程中的资源占用问题,需要本地部署一个文件服务,当前调研的是minio。后台项目代码使用laravel开发,sdk使用官方的aws-sdk-php-laravel。
1.开始使用之前需要先了解一下minio。
MinIO 是一个高性能的对象存储原生支持 Kubernetes 部署的解决方案。 MinIO 提供了一个 Amazon Web Services S3 兼容 API 并支持所有核心 S3 功能。
它采用了Apache License v2.0开源协议,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等。
了解一下AWS
Amazon Web Services (AWS) 是亚马逊(Amazon)公司推出的一种云计算服务,可以提供计算、存储、数据库、分析、人工智能、物联网、安全、开发工具、内容分发等服务。
AWS 提供了许多强大的服务,包括 EC2(弹性计算云)、S3(简单存储服务)、RDS(关系数据库服务)、Lambda(无服务器计算)、DynamoDB(高效 NoSQL 数据库)、EFS(文件存储服务)、SQS(消息队列服务)等。这些服务可以帮助开发者快速部署、管理和扩展他们的应用程序,并且不用担心服务器硬件、计算资源和存储设备等管理问题。
AWS 还提供了一个安全可靠的全球性基础设施,可以满足用户的各种需求。此外,它提供了强大的管理工具,方便用户监控和管理他们的应用程序。
AWS 的优势在于:
- 可伸缩性:AWS 可以根据需求动态增加或减少计算资源。
- 高可用性:AWS 提供了高可用性基础设施,保证了应用程序的可靠性和可用性。
二、安装
1.下载安装minio(这里使用的是docker单机安装)
docker pull minio/minio
下载完成后使用如下命令运行MinIO服务,注意使用--console-address指定MinIO Console的运行端口,否则会随机端口运行.
docker run -p 9000:9000 -p 9001:9001 --name minio \ -v /mydata/minio/data:/data \ -e MINIO_ROOT_USER=minioadmin \ -e MINIO_ROOT_PASSWORD=minioadmin \ -d minio/minio server /data --console-address ":9001"
端口号说明:9000端口是使用api操作minio时会用到的端口。
运行成功后就可访问MinIO Console的管理界面了,输入账号密码minioadmin:minioadmin即可登录,访问地址:http://localhost:9000
2.下载sdk(aws-sdk-php-laravel)
laravel项目下 根目录打开命令行界面执行命令
composer require aws/aws-sdk-php
代码配置
安装完毕后进行配置,续手动在config目录创建一个aws.php配置文件,内容如下
3.配置备注
①.区域标识一般是固定的,不需要较真(如下图)
②.credentials.key 和credentials.secret需要在管理界面获取,如图
参考:
https://juejin.cn/post/7134996289992785956
三、使用
1.简单使用
<?php
namespace Demo;
use Aws\Laravel\AwsFacade;
class DemoCode
{
/**
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function testS3()
{
$s3 = AwsFacade::createClient('s3');
//创建桶
$s3->createBucket(['Bucket' => $bucketName]);
//存储文件
$s3_return = $s3->putObject([
'Bucket' =>'test', //存储桶(需要提前创建,可在admin端或者使用命令创建桶)
'Key' =>'100186.csv', //文件名(包括后缀名,可以在这里进行分级,比如2023-01/test.scv,则会在桶内创建2023-01文件夹)
'Body' =>'123' //要保存的文件内容
]);
//下载文件
$s3_return = $s3->putObject([
'Bucket' =>'test', //存储桶
'Key' =>'100186.csv', //文件名(包括后缀名)
'SaveAs' =>'path/to/file' //本地路径
]);
$command = $s3->getCommand('GetObject', [
'Bucket' => 'test',
'key' => '20230203113909.csv'
]);
//有实效的连接
$request = $this->getClient()->createPresignedRequest($command, sprintf("+%d seconds", 600));
$url = (string)$request->getUri();
//长期连接(需要将桶的权限设置为public)
$url = $s3->getObjectUrl('test', '20230203113909.csv');
2. 桶操作
$s3 = AwsFacade::createClient('s3');
$list_buckets = $s3->listBuckets()['Buckets']; //列出当前oss用户所有的桶
$not_del = ['mytestingbucket', 'yedan-school-common'];
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
// 定义S3Client对象
$client = new S3Client([
'version' => 'latest',
'region' => 'your-bucket-region',
'credentials' => [
'key' => 'your-access-key-id',
'secret' => 'your-secret-access-key',
],
]);
// 定义要创建的Bucket名称
$bucketName = 'your-bucket-name';
try {
// 创建Bucket
$client->createBucket([
'Bucket' => $bucketName,
]);
// 设置Bucket ACL为public-read
$client->putBucketAcl([
'Bucket' => $bucketName,
'ACL' => 'public-read',
]);
// 输出Bucket创建成功的信息
echo "Bucket '{$bucketName}' created and set to public-read.\n";
} catch (AwsException $e) {
// 输出Bucket创建失败的信息
echo "Bucket '{$bucketName}' create failed: {$e->getMessage()}\n";
}
//删除桶
if(!empty($list_buckets)) { //要做非空判断
foreach ($list_buckets as $bucket) {
if (!in_array($bucket['Name'], $not_del)) { //排除掉不想被处理的桶
//获取桶内对象(文件)列表
$list_objects = $s3->listObjects(['Bucket' => $bucket['Name']])['Contents'];
if (!empty($list_objects)) { //要做非空判断
foreach ($list_objects as $object) {
//删除桶内文件,因为有文件的桶是不能删除的
$s3->deleteObject([
'Bucket'=>$bucket['Name'],
'Key'=>$object['Key']
]);
}
}
$s3->deleteBucket(['Bucket' => $bucket['Name']]); //删除桶
}
}
}
3. 分片上传
public function perUpload(Request $request)
{
$file = $request->file('file');
$s3 = AwsFacade::createClient('s3');
$fileTrueName = $file->getClientOriginalName();
$fileSize = $file->getSize();
$uploadResult = $s3->createMultipartUpload(['Bucket' => 'pretest', 'Key' => $fileTrueName]);
$offset = 0;
$perSize = 10 * 1024 * 1024;//每片分10mb
$perCount = intval($fileSize / $perSize);
$handle = fopen($file->getRealPath(), "r");
$i = 1;
while (!feof($handle)) {
$i == $perCount && $perSize = $fileSize;//最后一次的时候带上上不够一片的数据进行传输
$contents = fread($handle, $perSize);
// 处理文件内容
$params = [
'Bucket' => 'pretest',
'Key' => $fileTrueName,
'Body' => $contents, // 分片內容
'PartNumber' => $i, // 当前片数
'UploadId' => $uploadResult['UploadId'], //分片上传需要指定对应的上传id
];
$res = $s3->uploadPart($params);
$this->ETag[] = ['PartNumber' => $i, 'ETag' => $res['ETag']];
// 释放内存
unset($contents);
$i++;
}
$s3->completeMultipartUpload([
'Bucket' => 'pretest',
'Key' => $fileTrueName,
'MultipartUpload' => [
'Parts' => $this->ETag
],
'UploadId' => $uploadResult['UploadId'],
]);
fclose($handle);
}
四、拓展
avaScript中 s3协议的使用
Minio SDKs - JavaScript Client快速入门指南 - 《Minio Cookbook 中文版》 - 书栈网 · BookStack
Go语言中S3协议的使用
Minio SDKs - Golang Client API文档 - 《Minio Cookbook 中文版》 - 书栈网 · BookStack