1、原理
通过php curl请求minio, 操作minio,实现创建bucket,删除bucket,上传文件,删除文件,拷贝文件,设置存储桶策略等功能
2、原文件下载
原代码下载地址:
https://github.com/gusijin/minio-php-sdk
3、php操作minio方法
bucket操作,和object操作,包括:实现创建bucket,删除bucket,上传文件,删除文件,拷贝文件,设置存储桶策略
require_once 'Minio.php';
// Instantiate an Minio client.
$minioClient = Minio::getInstance();
// Bucket
$res = $minioClient->listBuckets();
$res = $minioClient->getBucket('file');
$res = $minioClient->createBucket('my'.rand(1000,9999));
$res = $minioClient->putBucketPolicy('my3833');
$res = $minioClient->deleteBucket('1111');
// File
// Upload a publicly accessible file. The file size and type are determined by the SDK.
$res = $minioClient->putObject( '/www/php/www.gsj.com/minio/Dingtalk_0913104610.jpg','my3833/0913104610_0007.jpg' );
$res = $minioClient->getObjectInfo('my-bucket/202210/1633571028_57616.jpg');
$res = $minioClient->getObjectUrl('/my-bucket/20220913104610_0001.jpg');
$res = $minioClient->getObject( '/my-bucket/20220913104610_0001.jpg');
$res = $minioClient->deleteObject('/my-bucket/20220913104610_0001.jpg');
$res = $minioClient->copyObject('/my-bucket/202210/1633571028_57616.jpg','/other-bucket/202210/1633571028_57616.jpg');
Minio.php 操作类
请将Minio.php文件中的endpoint,key, secret, Bucket修改为你的本地配置。
<?php
require_once 'Request.php';
require_once 'Response.php';
class Minio
{
const CODE_SUCCESS = 200;
const CODE_DEL_SUCCESS = 204;
private $endpoint = 'http://minio.xxx.com';
private $accessKey = 'minioadmin';
private $secretKey = 'minioadmin';
private $multiCurl;
private $curlOpts = [
CURLOPT_CONNECTTIMEOUT => 30,
CURLOPT_LOW_SPEED_LIMIT => 1,
CURLOPT_LOW_SPEED_TIME => 30
];
private static $instance;
public function __construct()
{
$this->multiCurl = curl_multi_init();
}
public function __destruct()
{
curl_multi_close($this->multiCurl);
}
/**
* 单例模式 获取实例
* @return Minio
*/
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 获取bucket列表
* @return array
*/
public function listBuckets()
{
$res = $this->requestBucket('GET', '');
if ($res && $res['code'] == self::CODE_SUCCESS) {
$res['data'] = ['bucket' => array_column($res['data']['Buckets']['Bucket'], 'Name')];
return $res;
} else {
return $res;
}
}
/**
* 获取bucket目录文件信息
* @param $bucket
* @return Response
*/
public function getBucket(string $bucket)
{
return $this->requestBucket('GET', $bucket);
}
/**
* 创建bucket目录
* @param $bucket
* @return bool true创建成功,false失败
*/
public function createBucket(string $bucket)
{
$res = $this->requestBucket('PUT', $bucket);
if ($res['code'] == self::CODE_SUCCESS) {
$this->putBucketPolicy($bucket);
return true;
}
return false;
}
/**
* 策略设置存储目录
* @param $bucket bucket目录
* @return mixed
*/
public function putBucketPolicy($bucket)
{
//该策略设置存储桶为只读
$policyReadOnly = '{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Effect": "Deny",
"Principal": {
"AWS": [
"*"
]
},
"Resource": [
"arn:aws:s3:::%s"
],
"Sid": ""
},
{
"Action": [
"s3:GetObject"
],
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Resource": [
"arn:aws:s3:::%s/*"
],
"Sid": ""
}
]
}
';
$policy = sprintf($policyReadOnly, $bucket, $bucket);
$res = $this->requestBucket('PUT', $bucket . '?policy', $policy);
return $res;
}
/**
* 删除bucket目录
* @param $bucket
* @return bool true删除成功,false失败
*/
public function deleteBucket(string $bucket)
{
$res = $this->requestBucket('DELETE', $bucket);
if ($res['code'] == self::CODE_DEL_SUCCESS) {
return true;
}
return false;
}
/**
* 上传文件
* @param string $file 本地需要上传的全路径文件
* @param string $uri 保存路径名称
* @return Response
*/
public function putObject(string $file, string $uri)
{
//判断bucket是否存在,不存在则创建
$uri = ltrim($uri, DIRECTORY_SEPARATOR);
$uriArr = explode(DIRECTORY_SEPARATOR, $uri);
$bucket = $uriArr[0];
$listBuckets = $this->listBuckets();
$bucketArr = $listBuckets['data']['bucket'];
if (!in_array($bucket, $bucketArr)) {
$this->createBucket($bucket);
}
$request = (new Request('PUT', $this->endpoint, $uri))
->setHeaders(['x-amz-acl' => 'public-read-write'])
->setFileContents(fopen($file, 'r'))
->setMultiCurl($this->multiCurl)
->setCurlOpts($this->curlOpts)
->sign($this->accessKey, $this->secretKey);
$res = $this->objectToArray($request->getResponse());
return $res;
}
/**
* 获取文件链接
* @param string $uri 保存路径名称
* @return string
*/
public function getObjectUrl(string $uri)
{
return $this->endpoint . $uri;
}
/**
* 获取文件类型,header中体现
* @param string $uri 保存路径名称
* @return Response code=200为文件存在
*/
public function getObjectInfo(string $uri)
{
$uri = ltrim($uri, DIRECTORY_SEPARATOR);
$request = (new Request('HEAD', $this->endpoint, $uri))
->setMultiCurl($this->multiCurl)
->setCurlOpts($this->curlOpts)
->sign($this->accessKey, $this->secretKey);
return $this->objectToArray($request->getResponse());
}
/**
* 获取文件 ,data返回二进制数据流
* @param string $uri 保存路径名称
* @param null $resource
* @param array $headers
* @return Response
*/
public function getObject(string $uri)
{
$uri = ltrim($uri, DIRECTORY_SEPARATOR);
$request = (new Request('GET', $this->endpoint, $uri))
->setMultiCurl($this->multiCurl)
->setCurlOpts($this->curlOpts)
->sign($this->accessKey, $this->secretKey);
return $this->objectToArray($request->getResponse());
}
/**
* 删除文件
* @param string $uri 保存路径名称
* @return bool true删除成功,false删除失败
*/
public function deleteObject(string $uri)
{
$uri = ltrim($uri, DIRECTORY_SEPARATOR);
$request = (new Request('DELETE', $this->endpoint, $uri))
->setMultiCurl($this->multiCurl)
->setCurlOpts($this->curlOpts)
->sign($this->accessKey, $this->secretKey);
$res = $this->objectToArray($request->getResponse());
if ($res['code'] == self::CODE_DEL_SUCCESS) {
return true;
}
return false;
}
/**
* 拷贝文件
* @param $fromObject 源文件
* @param $toObject 目标文件
* @return array|mixed {"code":200}
*/
public function copyObject($fromObject, $toObject)
{
$fromObject = ltrim($fromObject, DIRECTORY_SEPARATOR);
$toObject = ltrim($toObject, DIRECTORY_SEPARATOR);
//判断目标bucket是否存在,不存在则创建
$toArr = explode(DIRECTORY_SEPARATOR, $toObject);
$toBucket = $toArr[0];
$listBuckets = $this->listBuckets();
$bucketArr = $listBuckets['data']['bucket'];
if (!in_array($toBucket, $bucketArr)) {
$this->createBucket($toBucket);
}
$request = (new Request('PUT', $this->endpoint, $toObject))
->setHeaders(['x-amz-copy-source' => $fromObject])
->setMultiCurl($this->multiCurl)
->setCurlOpts($this->curlOpts)
->sign($this->accessKey, $this->secretKey);
$res = $this->objectToArray($request->getResponse());
return $res;
}
/**
* bucket目录请求
* @param string $method
* @param string $bucket
* @param array $headers
* @return mixed
*/
protected function requestBucket(string $method = 'GET', string $bucket = '', string $postFields = '')
{
$request = (new Request($method, $this->endpoint, $bucket))
->setPostField($postFields)
->setMultiCurl($this->multiCurl)
->setCurlOpts($this->curlOpts)
->sign($this->accessKey, $this->secretKey);
return $this->objectToArray($request->getResponse());
}
/**
* 对象转数组
* @param $object
* @return mixed
*/
private function objectToArray($object)
{
$arr = is_object($object) ? get_object_vars($object) : $object;
$returnArr = [];
foreach ($arr as $key => $val) {
$val = (is_array($val)) || is_object($val) ? $this->objectToArray($val) : $val;
$returnArr[$key] = $val;
}
return $returnArr;
}
}
Request.php 请求类
<?php
class Request
{
private $action;
private $endpoint;
private $uri;
private $headers;
private $curl;
private $response;
private $multi_curl;
private $secure;
public function __construct($action, $endpoint, $uri)
{
$this->action = $action;
$this->uri = $uri;
$url = parse_url($endpoint);
$this->endpoint = $url['host'];
$url['scheme'] === 'http' || $this->secure = true;
$this->headers = [
'Content-MD5' => '',
'Content-Type' => '',
'Date' => gmdate('D, d M Y H:i:s T'),
'Host' => $this->endpoint
];
$this->curl = curl_init();
$this->response = new Response();
$this->multi_curl = null;
}
/**
* 设置文件内容
* @param $file
* @return $this
*/
public function setFileContents($file)
{
if (is_resource($file)) {
$hash_ctx = hash_init('md5');
$length = hash_update_stream($hash_ctx, $file);
$md5 = hash_final($hash_ctx, true);
rewind($file);
$this->headers['Content-Type'] = mime_content_type($file);
curl_setopt($this->curl, CURLOPT_PUT, true);
curl_setopt($this->curl, CURLOPT_INFILE, $file);
curl_setopt($this->curl, CURLOPT_INFILESIZE, $length);
} else {
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $file);
$md5 = md5($file, true);
}
$this->headers['Content-MD5'] = base64_encode($md5);
return $this;
}
public function setPostField($postFields)
{
if (!empty($postFields)) {
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $postFields);
$md5 = md5($postFields, true);
$this->headers['Content-MD5'] = base64_encode($md5);
}
return $this;
}
public function setHeaders($headers)
{
if (!empty($headers)) {
$this->headers = array_merge($this->headers, $headers);
}
return $this;
}
/**
* 生成签名
* @param $access_key
* @param $secret_key
* @return $this
*/
public function sign($access_key, $secret_key)
{
$canonical_amz_headers = $this->getCanonicalAmzHeaders();
$string_to_sign = '';
$string_to_sign .= "{$this->action}\n";
$string_to_sign .= "{$this->headers['Content-MD5']}\n";
$string_to_sign .= "{$this->headers['Content-Type']}\n";
$string_to_sign .= "{$this->headers['Date']}\n";
if (!empty($canonical_amz_headers)) {
$string_to_sign .= implode($canonical_amz_headers, "\n") . "\n";
}
$string_to_sign .= "/{$this->uri}";
$signature = base64_encode(
hash_hmac('sha1', $string_to_sign, $secret_key, true)
);
$this->headers['Authorization'] = "AWS $access_key:$signature";
return $this;
}
/**
* multi_curl设置
* @param $mh
* @return $this
*/
public function setMultiCurl($mh)
{
$this->multi_curl = $mh;
return $this;
}
/**
* opt设置
* @param $curl_opts
* @return $this
*/
public function setCurlOpts($curl_opts)
{
curl_setopt_array($this->curl, $curl_opts);
return $this;
}
/**
* 获取返回内容
* @return Response
*/
public function getResponse()
{
$http_headers = array_map(
function ($header, $value) {
return "$header: $value";
},
array_keys($this->headers),
array_values($this->headers)
);
$url = "{$this->endpoint}/{$this->uri}";
if ($this->secure) {
$url = 'https://' . $url;
} else {
$url = 'http://' . $url;
}
curl_setopt_array($this->curl, [
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => $http_headers,
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_WRITEFUNCTION => [
$this->response, '__curlWriteFunction'
],
CURLOPT_HEADERFUNCTION => [
$this->response, '__curlHeaderFunction'
]
]);
switch ($this->action) {
case 'DELETE':
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
case 'HEAD':
curl_setopt($this->curl, CURLOPT_NOBODY, true);
break;
case 'POST':
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'POST');
break;
case 'PUT':
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'PUT');
break;
default:
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $this->action);
break;
}
if (isset($this->multi_curl)) {
curl_multi_add_handle($this->multi_curl, $this->curl);
$running = null;
do {
curl_multi_exec($this->multi_curl, $running);
curl_multi_select($this->multi_curl);
} while ($running > 0);
curl_multi_remove_handle($this->multi_curl, $this->curl);
} else {
curl_exec($this->curl);
}
$this->response->finalize($this->curl);
curl_close($this->curl);
return $this->response;
}
/**
* 头部处理
* @return array
*/
private function getCanonicalAmzHeaders()
{
$canonical_amz_headers = [];
foreach ($this->headers as $header => $value) {
$header = trim(strtolower($header));
$value = trim($value);
if (strpos($header, 'x-amz-') === 0) {
$canonical_amz_headers[$header] = "$header:$value";
}
}
ksort($canonical_amz_headers);
return $canonical_amz_headers;
}
}
Response.php 返回类
<?php
class Response
{
public $code; //200为成功
public $error;
public $headers;
public $data;
public function __construct()
{
$this->code = null;
$this->error = null;
$this->headers = [];
$this->data = null;
}
public function saveToResource($resource)
{
$this->data = $resource;
}
/**
* 返回内容回调
* @param $ch
* @param $data
* @return false|int
*/
public function __curlWriteFunction($ch, $data)
{
if (is_resource($this->data)) {
return fwrite($this->data, $data);
} else {
$this->data .= $data;
return strlen($data);
}
}
/**
* 头部信息回调
* @param $ch
* @param $data
* @return int
*/
public function __curlHeaderFunction($ch, $data)
{
$header = explode(':', $data);
if (count($header) == 2) {
list($key, $value) = $header;
$this->headers[$key] = trim($value);
}
return strlen($data);
}
/**
* 返回内容处理
* @param $ch
*/
public function finalize($ch)
{
if (is_resource($this->data)) {
rewind($this->data);
}
if (curl_errno($ch) || curl_error($ch)) {
$this->error = [
'code' => curl_errno($ch),
'message' => curl_error($ch),
];
} else {
$this->code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
if ($content_type == 'application/xml') {
$obj = simplexml_load_string($this->data, "SimpleXMLElement", LIBXML_NOCDATA);
$this->data = json_decode(json_encode($obj), true);
}
}
}
}