目录
[三、启动 minio 服务端](#三、启动 minio 服务端)
[2.在 C# 项目里引用](# 项目里引用)
[3.C# 项目配置 MinIO 服务端](# 项目配置 MinIO 服务端)
[(2)新建 LyWeb\Data\MinioHelper.cs](#(2)新建 LyWeb\Data\MinioHelper.cs)
一、前言
开源。类似于私人云存储(其实是存在本地,只是模拟云存储)。
用处:多个系统访问同一批文件;用户权限控制;大文件分片或并发上传;将来可直接换成S3云存储(即,将Minio换成S3时,代码几乎不用改)。
写这篇文章的主要目的:模拟S3云存储(实现上传存储、下载文件),因为minio兼容 Amazon S3接口,提供SDK支持,将来换成S3更方便。
二、官网下载
在官网 https://github.com/minio 中 下载minio和mc,在页面code下方一直滚动找到下载链接
(1)minio下载:https://dl.min.io/server/minio/release/
(2)mc下载:https://dl.min.io/client/mc/release/windows-amd64/mc.exe
(3)存放位置:自定义创建根目录,并在目录里创建bin、data、logs文件夹,将minio.exe和mc.exe放在bin文件夹里。
三、启动 minio 服务端
1.启动服务端
不要直接用exe,要用命名行。且在bin目录下打开cmd。
输入以下命令:
bash
# 1.设置服务端登录用户
setx MINIO_ROOT_USER 用户名
# 2.设置密码
setx MINIO_ROOT_PASSWORD 密码
# 3.启动服务端(两种命令)
#(1)
.\\minio.exe server E:\\minio\\data --console-address "127.0.0.1:9001" --address "127.0.0.1:9000"
#(2)
.\\minio.exe server E:\\LayuiAdmin\\minio\\data --console-address ":9001" --address ":9000"
# 该行命令的意义分别代表 启动 MinIO 服务器、指定数据目录、指定控制台地址、指定服务地址。
# 其中,控制台访问 <http://127.0.0.1:9001>。S3 API 服务访问 <http://127.0.0.1:9000>。
图例:

注意:首次使用启动命令打开的登录页面,需用默认账密登录(第二次打开使用自定义的账密)
2.使用服务端

输入账密即可登录。
首次使用启动命令打开的登录页面,需用默认账密 minioadmin:minioadmin登录(下一次使用启动命令打开的登录页面,则用自定义的账密)。
3.公开访问某桶文件
若C#项目想访问桶文件却没权限,F12的Network中显示路由是403,则需在服务端开启访问权限
即,在minio.exe所在的bin目录下打开cmd,输入命令:
bash
# 设置别名为local 方便后续操作(成功会显示:Added `local` successfully.)
mc alias set local <http://127.0.0.1:9000> admin admin2026
# 查看所有桶(会看到 0B bucket001/ )
mc ls local
# 设置可访问(成功会显示:Access permission for `local/bucket001` is set to `download`)
mc anonymous set download local/bucket001
# 其中,download 表示只允许匿名访问下载,不允许写入
# 恢复私有(撤销匿名访问)
mc anonymous set none local/bucket001
如图:

四、C#项目使用minio上传文件
1.安装Minio包
在 Visual Studio 的 NuGet 包管理器里搜索:Minio,并安装
安装成功后,在项目的.csproj文件中会自动生成:
cs
<PackageReference Include="Minio" Version="7.0.0" />
2.在 C# 项目里引用
using Minio;
using Minio.Exceptions;
3.C# 项目配置 MinIO 服务端
配置信息有:
S3 API 地址:http://127.0.0.1:9000
Access Key:登录用户名
Secret Key:登录密码
配置例子如下:
cs
public class MinioController : Controller
{
private readonly IMinioClient _minioClient; // 使用minio模拟上传文件到云端
public MinioController()
{
_minioClient = new MinioClient()
.WithEndpoint("127.0.0.1", 9000) // S3 API地址 和 端口
.WithCredentials("admin", "admin2026") // 账号密码
.WithSSL(false) // 是否 https
.Build();
}
}
4.初始化前端页面
(1)后端跳转到前端页面
cs
[HttpGet]
public IActionResult Minio()
{
return View("~/Views/MinioFile/Minio.cshtml");
}
(2)前端页面
cpp
@{
ViewData["Title"] = "上传云端";
}
<div class="min-vh-100 d-flex justify-content-center align-items-center">
<div class="card">
<button type="button" class="layui-btn" id="uploadBtn">上传文件</button>
<img id="avatarImg" src="" style="width:150px;height:150px;border:none;" />
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" /> <!--配合 div asp-validation-summary-->
<script>
layui.use(['upload', 'layer'], function () {
var upload = layui.upload;
var layer = layui.layer;
upload.render({
elem: '#uploadBtn',
url: '/Minio/Upload',
accept: 'file',
choose: function(obj){
console.log("已选中文件");
},
done: function (res) {
if (res.res) {
layer.msg("上传成功");
document.getElementById("avatarImg").src = res.data;
} else {
layer.msg("上传失败:" + res.msg);
}
}
});
});
</script>
}
5.controller.cs上传文件
cs
using Microsoft.AspNetCore.Mvc;
using Minio;
using Minio.DataModel.Args;
namespace LyWeb.Controllers
{
public class MinioHelper
{
public string Endpoint { get; set; }
public int Port { get; set; }
public string AccessKey { get; set; }
public string SecretKey { get; set; }
public bool UseSSL { get; set; }
}
public class MinioController : Controller
{
private readonly IMinioClient _minioClient; // 使用minio模拟上传文件到云端
public MinioController()
{
_minioClient = new MinioClient()
.WithEndpoint("127.0.0.1", 9000) // S3 API地址 和 端口
.WithCredentials("admin", "admin2026") // 账号密码
.WithSSL(false) // 是否 https
.Build();
}
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Minio()
{
return View("~/Views/Account/Minio.cshtml");
}
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
// 检查文件是否为空
if (file == null || file.Length == 0)
{
return Ok(new { res = false, msg = "请选择文件" });
}
try
{
// 1.1 配置桶名
var bucketName = "bucket001";
// 1.2 创建桶名(若存在,则直接跳过创建)
bool bk = await _minioClient.BucketExistsAsync( new BucketExistsArgs().WithBucket(bucketName) );
if (!bk)
{
await _minioClient.MakeBucketAsync( new MakeBucketArgs().WithBucket(bucketName) );
}
// 2. 生成唯一文件名(防重复)
var fileExt = Path.GetExtension(file.FileName); // 获取文件名后缀(如.png)
var objectName = Guid.NewGuid().ToString() + fileExt; // 生成唯一值(如 DBKFGFG01bdjksf.png)
// 3. 上传
using (var stream = file.OpenReadStream())
{
await _minioClient.PutObjectAsync(
new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
.WithStreamData(stream)
.WithObjectSize(file.Length)
.WithContentType(file.ContentType)
);
}
// 4. 返回访问地址
var url = $"http://127.0.0.1:9000/{bucketName}/{objectName}";
return Ok(new { res = true, data= url });
}
catch (Minio.Exceptions.ConnectionException ex)
{
return Ok(new { res = false, msg = $"无法连接到 MinIO 服务" });
}
catch (Minio.Exceptions.AccessDeniedException ex)
{
return Ok(new { res = false, msg = $"MinIO 认证失败" });
}
catch (Exception ex)
{
return Ok(new { res = false, msg = $"Minio服务 连接失败 / DNS错误 / 认证失败: {ex.Message}" });
}
}
}
}
6.结合DI注册(可跳过)
前面的内容已经可以实现模拟S3文件上传,若想统一设置minio服务的配置,则可接着改
(1)在appsettings.json中补充
javascript
"Minio": {
"Endpoint": "127.0.0.1", // 设置 minio 端口
"Port": 9000, // 设置 minio API地址
"AccessKey": "admin", // 设置账号
"SecretKey": "admin2026", // 设置密码
"UseSSL": false // 是否使用 https
},
(2)新建 LyWeb\Data\MinioHelper.cs
javascript
namespace LyWeb.Data
{
public class MinioHelper
{
public string Endpoint { get; set; }
public int Port { get; set; }
public string AccessKey { get; set; }
public string SecretKey { get; set; }
public bool UseSSL { get; set; }
}
}
(3)在programs.cs中补充
javascript
using LyWeb.LyData;
using Microsoft.Extensions.Options;
using Minio;
#region 注册Minio
// 绑定配置(把 appsettings.json Minio 节点绑定到 MinioHelper 强类型类,方便获取配置)
builder.Services.Configure<MinioHelper>(
builder.Configuration.GetSection("Minio")
);
// 注册 MinioClient
builder.Services.AddSingleton<IMinioClient>(sp =>
{
// 前面builder.Services.Configure<MinioHelper>注册到依赖注入容器,即可通过 IOptions<MinioHelper> 随时获取配置值
var options = sp.GetRequiredService<IOptions<MinioHelper>>().Value;
return new MinioClient()
.WithEndpoint(options.Endpoint, options.Port) // 设置 API地址 和 端口
.WithCredentials(options.AccessKey, options.SecretKey) // 设置账号密码
.WithSSL(options.UseSSL) // 是否使用 https
.Build(); // 构建客户端对象
});
#endregion
(4)修改controller.cs
cs
//todo-ly:0319
using LyWeb.Data;
using Microsoft.AspNetCore.Mvc;
using Minio;
using Minio.DataModel.Args;
using Microsoft.Extensions.Options;
namespace LyWeb.Controllers
{
public class MinioController : Controller
{
private readonly IMinioClient _minioClient; // 使用minio模拟上传文件到云端
private readonly MinioHelper _minioConfig; // 获取minio配置项的值
public MinioController(IMinioClient minioClient, IOptions<MinioHelper> optMinio)
{
_minioClient = minioClient;
_minioConfig = optMinio.Value;
}
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Minio()
{
return View("~/Views/Account/Minio.cshtml");
}
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
// 检查文件是否为空
if (file == null || file.Length == 0)
{
return Ok(new { res = false, msg = "请选择文件" });
}
try
{
// 1.1 配置桶名
var bucketName = "bucket001";
// 1.2 创建桶名(若存在,则直接跳过创建)
bool bk = await _minioClient.BucketExistsAsync( new BucketExistsArgs().WithBucket(bucketName) );
if (!bk)
{
await _minioClient.MakeBucketAsync( new MakeBucketArgs().WithBucket(bucketName) );
}
// 2. 生成唯一文件名(防重复)
var fileExt = Path.GetExtension(file.FileName); // 获取文件名后缀(如.png)
var objectName = Guid.NewGuid().ToString() + fileExt; // 生成唯一值(如 DBKFGFG01bdjksf.png)
// 3. 上传
using (var stream = file.OpenReadStream())
{
await _minioClient.PutObjectAsync(
new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
.WithStreamData(stream)
.WithObjectSize(file.Length)
.WithContentType(file.ContentType)
);
}
// 4. 返回访问地址
var url = $"{(_minioConfig.UseSSL ? "https" : "http")}://{_minioConfig.Endpoint}:{_minioConfig.Port}/{bucketName}/{objectName}";
return Ok(new { res = true, data= url });
}
catch (Minio.Exceptions.ConnectionException ex)
{
return Ok(new { res = false, msg = $"无法连接到 MinIO 服务" });
}
catch (Minio.Exceptions.AccessDeniedException ex)
{
return Ok(new { res = false, msg = $"MinIO 认证失败" });
}
catch (Exception ex)
{
return Ok(new { res = false, msg = $"Minio服务 连接失败 / DNS错误 / 认证失败: {ex.Message}" });
}
}
}
}
7.实现下载
(1)前端补充
html
<button class="layui-btn" id="downloadBtn">下载</button>
<script>
layui.use(['upload', 'layer'], function () {
var upload = layui.upload;
var layer = layui.layer;
document.getElementById("downloadBtn").addEventListener("click", function () {
// 要下载的文件
var downfile = encodeURIComponent("XXX.png");
// 先请求检查接口
fetch(`/Minio/CheckFile?objectName=${downfile}`)
.then(res => res.json())
.then(res => {
if (!res.res) {
layer.msg("文件不存在或未上传");
return;
}
// 再触发下载
window.location.href = `/Minio/DownLoad?objectName=${downfile}`;
layer.msg("文件下载成功");
});
});
}
</script>
(2)后端补充
cs
[HttpGet]
public async Task<IActionResult> CheckFile(string objectName)
{
var bucketName = "bucket001";
// 检查是否文件存在
try
{
await _minioClient.StatObjectAsync(
new StatObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName) // "XXX.png"
);
return Ok(new { res = true });
}
catch (Exception e)
{
return Ok(new { res = false, msg = $"Error occurred: {e.Message}" });
}
}
[HttpGet]
public async Task<IActionResult> DownLoad(string objectName)
{
var bucketName = "bucket001";
try
{
var ms = new MemoryStream();
// 获取文件
await _minioClient.GetObjectAsync(
new GetObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName) // "XXX.png"
.WithCallbackStream(stream =>
{
stream.CopyTo(ms);
})
);
ms.Position = 0;
// 返回文件给浏览器(让浏览器下载)
return File(ms, "application/octet-stream", objectName);
}
catch (Exception e)
{
return Ok(new { res = false, msg = $"Error occurred: {e.Message}" });
}
}