这篇文章里,我们介绍在.net core webapi项目中操作MinIO。
首先要创建一个桶,命名为demo
英文文档看不太顺畅,在网上找了一个api中文文档,可供参考
.NET Client API参考文档 - MinIO 帮助文档 - 开发文档 - 文江博客
创建桶
点击Buckets→Create Bucket创建桶
桶名可以命名为demo,点击确认
创建用户和秘钥
创建用户
创建AccessKey和SecretKey,点击Identity→Users→点击上一步创建的用户
把上面的AccessKey和SecretKey的值保存下来,可以在配置文件中使用。
创建WebApi项目
创建个webapi项目,并添加Minio的NuGet包,这里我选3.1.13版本
将Minio注入进来,并将minio连接信息配置在appsetting,json文件中Startup.cs
public void ConfigureServices(IServiceCollection services)
{services.AddHttpClient();#region minio客户端注入var minioClient = new MinioClient(Configuration["MinIO:Endpoint"], Configuration["MinIO:AccessKey"], Configuration["MinIO:SecretKey"]);services.AddSingleton(minioClient);#endregion#region 带权限的swaggerservices.AddSwaggerGen(options =>{//定义api文档options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My API", Version = "v1" });//包含vs生成的注释文档//var xmlPath = Path.Combine(webHostEnvironment.ContentRootPath, Assembly.GetExecutingAssembly().GetName().Name + ".xml");//if (File.Exists(xmlPath))//{// options.IncludeXmlComments(xmlPath, true);//}//描述安全信息options.AddSecurityDefinition(CookieAuthenticationDefaults.AuthenticationScheme, new OpenApiSecurityScheme(){Name = CookieAuthenticationDefaults.AuthenticationScheme,Scheme = CookieAuthenticationDefaults.AuthenticationScheme});});#endregionservices.AddControllers();#region 不带权限的swagger//services.AddSwaggerGen(c =>//{// c.SwaggerDoc("v1", new OpenApiInfo { Title = "MinioDemo.WebApi", Version = "v1" });//});#endregionservices.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();services.AddAuthorization();}
appsetting,json
"AccessKey"和"SecretKey"可以用账号密码,也可以用配置中,新加用户中的配置信息
{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*",//minIO配置"MinIO": {//服务器IP"Endpoint": "localhost:9090",//账号"AccessKey": "minioadmin",//密码"SecretKey": "minioadmin",//默认存储桶"Bucket": "demo",//保存文件的根目录"BucketDirectory": "D:\\aaa\\bbb\\ccc"},"Kestrel": {"EndPoints": {"Http": {"Url": "http://*:5000"}}},"PDM": {"Secret": "","Uri": ""}
}
完整上传、下载代码
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System;
using Minio;
using System.Linq;
using Minio.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Minio.DataModel;
using System.Reactive.Linq;
using System.Data;
using System.Reactive.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using Newtonsoft.Json;namespace MinIOTest.Controllers
{[ApiController][Route("api/[controller]/[action]")]//[Authorize]public class MinIOController : ControllerBase{string _bucketName = string.Empty;//默认桶private readonly MinioClient _client;private readonly IConfiguration _configuration;public MinIOController(MinioClient client,IConfiguration configuration){_client = client;_configuration = configuration;_bucketName = configuration["MinIO:Bucket"];}#region 测试[HttpGet]public async Task<dynamic> test(){return new { bb = "bbb", cc = "ccc" };}#endregion#region 上传文件/// <summary>/// 上传文件/// </summary>/// <param name="filePath">文件保存路径</param>/// <param name="files">文件</param>/// <returns></returns>[HttpPost][AllowAnonymous]public async Task<dynamic> UploadFile(string filePath, List<IFormFile> files){long size = files.Sum(f => f.Length);try{bool isExists = await _client.BucketExistsAsync(_bucketName);//桶是否存在//如果桶不存在则创建桶if (!isExists){await _client.MakeBucketAsync(_bucketName);}foreach (var formFile in files){string saveFileName = $"{Path.GetFileName(formFile.FileName)}";//存储 的文件名string objectName = $"/{filePath}/{saveFileName}";//文件保存路径if (formFile.Length > 0){Stream stream = formFile.OpenReadStream();await _client.PutObjectAsync(_bucketName,objectName,stream,formFile.Length,formFile.ContentType);}}}catch (MinioException ex){_logger.LogError($"文件上传错误:{ex}");return Ok(new { Success = false, Message = $"文件上传错误:{ex.Message}" });}return Ok(new { Success = true, Count = files.Count, Size = size });}#endregion 上传文件#region 下载文件/// <summary>/// 下载文件/// </summary>/// <param name="fileName">文件地址</param>/// <returns></returns>[HttpGet][AllowAnonymous]public async Task<IActionResult> DownloadFile(string fileName){var memoryStream = new MemoryStream();try{await _client.StatObjectAsync(_bucketName, fileName);await _client.GetObjectAsync(_bucketName, fileName,(stream) =>{stream.CopyTo(memoryStream);});memoryStream.Position = 0;}catch (MinioException ex){_logger.LogError($"下载附件发生错误:{ex}");return Ok(new { Success = false, Message = $"下载附件发生错误:{ex.Message}" });}return File(memoryStream, GetContentType(fileName));}#endregion 下载文件#region 获取文件ContentType类型private static string GetContentType(string fileName){if (fileName.Contains(".jpg")){return "image/jpg";}else if (fileName.Contains(".jpeg")){return "image/jpeg";}else if (fileName.Contains(".png")){return "image/png";}else if (fileName.Contains(".gif")){return "image/gif";}else if (fileName.Contains(".pdf")){return "application/pdf";}else if (fileName.Contains(".docx")){return "application/msword";}else if (fileName.Contains(".txt")){return "text/plain";}else{return "application/octet-stream";}}#endregion 获取文件类型#region 获取指定文件目录/// <summary>/// 获取指定文件目录/// </summary>/// <param name="prefixArr">文件路径(格式:["工程图纸/001","工程图纸/002"]) </param>/// <param name="fileName">文件名,模糊查询</param>/// <returns></returns>[HttpPost]public async Task<object> GetFileListAsycn(string[] prefixArr, string fileName){try{bool found = await _client.BucketExistsAsync(_bucketName);if (found){List<Item> filePathList = new List<Item>();foreach (string prefix in prefixArr){var files = _client.ListObjectsAsync(_bucketName, prefix, true);var filePaths = files.ToList().Wait();filePathList.InsertRange(filePathList.Count(), filePaths);}if (!string.IsNullOrEmpty(fileName)){filePathList = filePathList.Where(d => d.Key.Split('/').Last().Contains(fileName)).ToList();}return Ok(new { Success = true, Count = filePathList.Count(), Data = filePathList });}else{return Ok(new { Success = false, Data = $"桶[{_bucketName}]不存在" });}}catch (MinioException ex){_logger.LogError($"MinIO发生错误:{ex}");return Ok(new { Success = false, Data = $"MinIO发生错误:{ex.Message}" });}}#endregion 获取指定文件目录#region 获取最上层目录/// <summary>/// 获取最上层目录/// </summary>/// <returns></returns>[HttpGet]public async Task<object> GetDirectoryAsycn(){try{bool found = await _client.BucketExistsAsync(_bucketName);if (found){var files = _client.ListObjectsAsync(_bucketName, "", false);var fileDirectory = files.ToList().Wait();foreach (var file in fileDirectory){file.Key = file.Key.Replace("/", "");}return Ok(new { Success = true, Data = fileDirectory });}else{return Ok(new { Success = false, Data = $"桶[{_bucketName}]不存在" });}}catch (MinioException ex){_logger.LogError($"MinIO发生错误:{ex}");return Ok(new { Success = false, Data = $"MinIO发生错误:{ex}" });}}#endregion 获取最上层目录 }
}
运行程序,如下图,上传接口
下载接口,输入文件地址
获取某个文件夹下的所有文件目录,递归获取
获取文件列表接口这里要要注意下,因为ListObjectsAsync这个接口是异步的,当自己写的接口执行完的时候,调用MinIO获取文件列表的接口还没执行完,所以,获取MinIO文件列表接口(ListObjectsAsync),要使用方法Wait()改成同步,即
var files = _client.ListObjectsAsync(_bucketName, prefix, true);
var filePaths = files.ToList().Wait();
这样,才能获取全部路径
获取根目录,非递归
其他api接口方法,查看官方文档,文档地址
Windows 的 MinIO 对象存储 — MinIO Object Storage for Windows
.NET Client API参考文档 - MinIO 帮助文档 - 开发文档 - 文江博客
上一篇:MinIO (一)安装并生成windows服务
下一篇:MinIO (三) 使用Webhook实时同步文件