本篇内容主要记录.NET中使用MinIO文件存储的常用方法操作类,有关MioIO的概念和优缺点就不写了,百度都有。

MinIO文件服务器的搭建请查看:Java集成MinIO,项目中使用NuGet搜索MinIO进行安装即可。
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Minio;
using Minio.DataModel;
using Minio.Exceptions;

namespace ConsoleApplication1
{
    public static class MinioHelper
    {
        static readonly string _endPoint  = "127.0.0.1:9001";
        static readonly string _accessKey = "minioadmin";
        static readonly string _secretKey = "minioadmin";
        static readonly bool _isSSL = false;
        private static readonly MinioClient _minioClient = new MinioClient()
                            .WithEndpoint(_endPoint)
                            .WithCredentials(_accessKey,_secretKey)
                            .WithSSL(_isSSL)
                            .Build();

        /// <summary>
        ///  创建minio客户端连接
        /// </summary>
        /// <param name="endPoint">127.0.0.1:9001</param>
        /// <param name="accessKey">minioadmin</param>
        /// <param name="secretKey">minioadmin</param>
        /// <param name="withSsl">是否是http</param>
        /// <returns></returns>
        public static MinioClient Create(string endPoint, string accessKey, string secretKey, bool withSsl = false)
        {
            MinioClient client = new MinioClient()
                .WithEndpoint(endPoint)
                .WithCredentials(accessKey,
                    secretKey);
            if (withSsl)
            {
                client = client.WithSSL();
            }

            return client.Build();
        }

        #region 操作存储桶

        /// <summary>
        /// 创建存储桶
        /// <example >
        ///     <code >
        ///         MinioHelper.MakeBucket(minio, buckName).Wait();    
        ///     </code>
        /// </example>
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name = "loc" > 可选参数 </param >
        /// <returns ></returns >
        public static Task CreateBucket(string bucketName, string loc = "us-east-1")
        {
            try
            {
                bool found = BucketExists(bucketName);
                if (found)
                {
                    throw new Exception($"存储桶[{bucketName}]已存在");
                }

                MakeBucketArgs args = new MakeBucketArgs()
                    .WithBucket(bucketName)
                    .WithLocation(loc);

                return _minioClient.MakeBucketAsync(args);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 校验桶是否存在,如果不存在则报错
        /// <example>
        /// 调用示例
        /// <code>
        ///     bool exists = MinioHelper.BucketExists(minio, buckName);
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="bucketName"></param>
        /// <exception cref="Exception"></exception>
        public static void CheckBucket(string bucketName)
        {
            bool exists = BucketExists(bucketName);
            if (!exists)
            {
                throw new BucketNotFoundException(bucketName, "存储桶不存在");
            }
            
        }

        /// <summary>
        /// 列出所有的存储桶
        /// </summary>
        /// <example>
        /// <code>
        /// abc</code>
        /// </example>
        /// <returns></returns>
        public static ListAllMyBucketsResult ListBuckets()
        {
            Task<ListAllMyBucketsResult> data = _minioClient.ListBucketsAsync();
            data.Wait();
            return data.Result;
        }

        /// <summary>
        /// 检查存储桶是否存在
        /// </summary>
        /// <example>
        /// <code>
        /// var data = MinioHelper.ListBuckets(minio);
        /// </code>
        /// </example>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="cancellationToken">取消标记</param>
        /// <returns></returns>
        public static bool BucketExists(string bucketName,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                BucketExistsArgs args = new BucketExistsArgs()
                    .WithBucket(bucketName);

                Task<bool> bucketExistTask = _minioClient.BucketExistsAsync(args, cancellationToken);
                Task.WaitAll(bucketExistTask);
                return bucketExistTask.Result;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 删除一个存储桶
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="cancellationToken">默认不填</param>
        /// <returns></returns>
        public static Task RemoveBucket(string bucketName,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                CheckBucket(bucketName);
                RemoveBucketArgs args = new RemoveBucketArgs()
                    .WithBucket(bucketName);
                return _minioClient.RemoveBucketAsync(args, cancellationToken);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>列出存储桶里的对象
        /// 列出存储桶里的对象
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="prefix">对象的前缀</param>
        /// <param name="recursive">true代表递归查找,false代表类似文件夹查找,以'/'分隔,不查子文件夹</param>
        public static IObservable<Item> ListObjects(string bucketName, string prefix = null,
            bool recursive = true)
        {
            try
            {
                ListObjectsArgs args = new ListObjectsArgs()
                    .WithBucket(bucketName)
                    .WithPrefix(prefix)
                    .WithRecursive(recursive);

                IObservable<Item> data = _minioClient.ListObjectsAsync(args);

                return data;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region 操作文件对象

        public static bool FileExist(string bucketName, string objectName)
        {
            try
            {
                var obj = new StatObjectArgs().WithBucket(bucketName)
                    .WithObject(objectName);
                var objStat = _minioClient.StatObjectAsync(obj);
                objStat.Wait();
            }
            catch (AggregateException e)
            {
                foreach (var item in e.InnerExceptions)
                {
                    if (item is ObjectNotFoundException)
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 从桶下载文件到流
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <param name="sse">默认不填</param>
        /// <param name="cb">默认不填</param>
        /// <returns></returns>
        public static Task<ObjectStat> FGetObject(string bucketName, string objectName,
            ServerSideEncryption sse = null, Action<Stream> cb = null)
        {
            CheckBucket(bucketName);
            try
            {
                GetObjectArgs args = new GetObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithServerSideEncryption(sse)
                    .WithCallbackStream(cb);
                return _minioClient.GetObjectAsync(args);
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 从桶下载文件到本地
        /// </summary>
        /// <param name="bucketName"></param>
        /// <param name="objectName"></param>
        /// <param name="fileName"></param>
        /// <param name="sse"></param>
        /// <returns></returns>
        public static Task<ObjectStat> FGetObject(string bucketName, string objectName,
            string fileName,ServerSideEncryption sse = null)
        {
            CheckBucket(bucketName);

            if (File.Exists(fileName))
            {
                File.Delete(fileName);
            }

            return FGetObject(bucketName, objectName, sse, stream =>
            {
                using (FileStream fileStream = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write))
                {
                    stream.CopyTo(fileStream);
                }
            });
        }

        /// <summary>
        /// 上传本地文件至存储桶
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <param name="fileName">本地路径</param>
        /// <param name="contentType">contentType:默认不填</param>
        /// <param name="metaData">metaData:默认不填</param>
        /// <param name="sse">默认不填</param>
        /// <returns></returns>
        public static Task FPutObject(string bucketName, string objectName, string fileName,
            string contentType = "application/octet-stream", Dictionary<string, string> metaData = null,
            ServerSideEncryption sse = null)
        {
            CheckBucket(bucketName);
            try
            {
                //var data= minio.PutObjectAsync(bucketName, objectName, fileName, contentType: "application/octet-stream");
                PutObjectArgs args = new PutObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithFileName(fileName)
                    .WithContentType(contentType)
                    .WithHeaders(metaData)
                    .WithServerSideEncryption(sse);
                return _minioClient.PutObjectAsync(args);
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region Presigned操作

        /// <summary>生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
        /// 生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <param name="expiresInt">失效时间(以秒为单位),默认是7天,不得大于七天</param>
        /// <returns></returns>
        public static Task<string> PresignedGetObject(string bucketName, string objectName,
            int expiresInt = 1000)
        {
            CheckBucket(bucketName);
            try
            {
                //Dictionary<string, string> reqParams = new Dictionary<string, string> { { "response-content-type", "application/json" } };
                PresignedGetObjectArgs args = new PresignedGetObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    //.WithHeaders(reqParams)
                    .WithExpiry(expiresInt);
                //.WithRequestDate(DateTime.Now.ToUniversalTime());
                return _minioClient.PresignedGetObjectAsync(args);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 生成一个给HTTP PUT请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <param name="expiresInt">失效时间(以秒为单位),默认是7天,不得大于七天</param>
        /// <returns></returns>
        public static Task<string> PresignedPutObject(string bucketName, string objectName,
            int expiresInt = 1000)
        {
            CheckBucket(bucketName);

            try
            {
                //string presignedUrl = await minio.PresignedPutObjectAsync(bucketName, objectName, expiresInt);
                //Ret = presignedUrl;
                //flag = true;
                PresignedPutObjectArgs args = new PresignedPutObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithExpiry(expiresInt);
                return _minioClient.PresignedPutObjectAsync(args);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 允许给POST请求的presigned URL设置策略,比如接收对象上传的存储桶名称的策略,key名称前缀,过期策略。
        /// </summary>
        /// <param name="form">对象的post策略</param>
        /// <returns></returns>
        public static async Task<(Uri, Dictionary<string, string>)> PresignedPostPolicy(PostPolicy form)
        {
            try
            {
                //例如
                //PostPolicy form = new PostPolicy();
                //DateTime expiration = DateTime.UtcNow;
                //form.SetExpires(expiration.AddDays(10));
                //form.SetKey("my-objectname");
                //form.SetBucket("my-bucketname");
                (Uri, Dictionary<string, string>) data = await _minioClient.PresignedPostPolicyAsync(form);
                return data;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region 操作对象

        /// <summary>
        /// 下载对象指定区域的字节数组做为流。offset和length都必须传
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <param name="offset">offset是起始字节的位置</param>
        /// <param name="length">length是要读取的长度</param>
        /// <param name="callback">处理流的回调函数</param>
        /// <returns></returns>
        public static async Task GetObjectAsync(string bucketName, string objectName, long offset,
            long length, Action<Stream> callback)
        {
            CheckBucket(bucketName);

            try
            {
                StatObjectArgs args = new StatObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithServerSideEncryption(null);

                await _minioClient.StatObjectAsync(args);

                GetObjectArgs objArgs = new GetObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithCallbackStream(callback)
                    .WithOffsetAndLength(offset, length)
                    .WithServerSideEncryption(null);
                await _minioClient.GetObjectAsync(objArgs);
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 通过Stream上传对象
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <param name="data">要上传的Stream对象</param>
        /// <param name="size">流的大小</param>
        /// <param name="contentType">文件的Content type,默认是"application/octet-stream"</param>
        /// <param name="metaData">元数据头信息的Dictionary对象,默认是null</param>
        /// <returns></returns>
        public static async Task PutObjectAsync(string bucketName, string objectName, Stream data,
            long size, string contentType = "application/octet-stream", Dictionary<string, string> metaData = null)
        {
            CheckBucket(bucketName);
            try
            {
                PutObjectArgs args = new PutObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithStreamData(data)
                    .WithObjectSize(size)
                    .WithContentType(contentType)
                    .WithHeaders(metaData)
                    .WithServerSideEncryption(null);
                await _minioClient.PutObjectAsync(args);
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 从objectName指定的对象中将数据拷贝到destObjectName指定的对象
        /// </summary>
        /// <param name="fromBucketName">源存储桶名称</param>
        /// <param name="fromObjectName">源存储桶中的源对象名称</param>
        /// <param name="destBucketName">目标存储桶名称</param>
        /// <param name="destObjectName">要创建的目标对象名称,如果为空,默认为源对象名称</param>
        /// <param name="copyConditions">拷贝操作的一些条件Map</param>
        /// <param name="sseSrc">默认不填</param>
        /// <param name="sseDest">默认不填</param>
        /// <param name="metadata">默认不填</param>
        /// <returns></returns>
        public static async Task CopyObject(string fromBucketName, string fromObjectName,
            string destBucketName, string destObjectName, CopyConditions copyConditions = null,
            ServerSideEncryption sseSrc = null, ServerSideEncryption sseDest = null,
            Dictionary<string, string> metadata = null)
        {
            CheckBucket(fromBucketName);
            CheckBucket(destBucketName);

            try
            {
                CopySourceObjectArgs cpSrcArgs = new CopySourceObjectArgs()
                    .WithBucket(fromBucketName)
                    .WithObject(fromObjectName)
                    .WithCopyConditions(copyConditions)
                    .WithServerSideEncryption(sseSrc);
                CopyObjectArgs args = new CopyObjectArgs()
                    .WithBucket(destBucketName)
                    .WithObject(destObjectName)
                    .WithCopyObjectSource(cpSrcArgs)
                    .WithHeaders(metadata)
                    .WithServerSideEncryption(sseDest);
                await _minioClient.CopyObjectAsync(args);
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 删除一个对象
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectName">存储桶里的对象名称</param>
        /// <returns></returns>
        public static Task RemoveObject(string bucketName, string objectName)
        {
            CheckBucket(bucketName);
            try
            {
                RemoveObjectArgs args = new RemoveObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName);
                return _minioClient.RemoveObjectAsync(args);
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// 删除多个对象
        /// </summary>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectsList">含有多个对象名称的IEnumerable</param>
        /// <returns></returns>
        public static async Task<bool> RemoveObjects(string bucketName, List<string> objectsList)
        {
            CheckBucket(bucketName);
            bool flag = false;
            try
            {
                if (objectsList != null)
                {
                    flag = true;
                    RemoveObjectsArgs args = new RemoveObjectsArgs()
                        .WithBucket(bucketName)
                        .WithObjects(objectsList);
                    await _minioClient.RemoveObjectsAsync(args);
                }
            }
            catch (MinioException e)
            {
                throw new Exception(e.Message);
            }

            return flag;
        }

        #endregion
    }
}
最后修改:2022 年 12 月 23 日
去码头整点薯片?