MINIO是提供.NET SDK的,但是這么老的版本沒(méi)找到,于是使用http的方式直接調(diào)用,方便簡(jiǎn)單。
我這里需求不復(fù)雜,只需要上傳下載刪除即可,如果后續(xù)有需求再補(bǔ)充方法。

核心代碼MinioHttpOperatorDemo如下:
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Collections.Specialized;
using System.Security.Cryptography;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace MinioHttpOperatorDemo
{
public class MinioHttpOperator
{
private readonly string _minioEndpoint;
private readonly string _accessKey;
private readonly string _secretKey;
private readonly string _region;
private readonly string _service;
public MinioHttpOperator(string minioEndpoint, string accessKey, string secretKey, string region = "cn-north-1", string service = "s3")
{
_minioEndpoint = minioEndpoint.TrimEnd('/');
_accessKey = accessKey;
_secretKey = secretKey;
_region = region;
_service = service;
if (string.IsNullOrEmpty(_accessKey) || string.IsNullOrEmpty(_secretKey))
{
throw new ArgumentNullException("AccessKey 和 SecretKey 不能為空,因?yàn)樾枰M(jìn)行認(rèn)證。");
}
}
public bool UploadFile(string bucketName, string objectName, string filePath, string contentType = "application/octet-stream")
{
try
{
if (!File.Exists(filePath))
{
Console.WriteLine($"錯(cuò)誤:文件未找到,路徑:{filePath}");
return false;
}
string url = $"{_minioEndpoint}/{bucketName}/{objectName}";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "PUT";
request.ContentType = contentType;
string contentHash;
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using (SHA256 sha256 = new SHA256Managed())
{
byte[] hashBytes = sha256.ComputeHash(fileStream);
contentHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
fileStream.Position = 0;
request.ContentLength = fileStream.Length;
SignRequest(request, bucketName, objectName, contentHash);
using (Stream requestStream = request.GetRequestStream())
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
requestStream.Write(buffer, 0, bytesRead);
}
}
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
Console.WriteLine($"成功上傳文件 {objectName} 到桶 {bucketName}。");
return true;
}
else
{
Console.WriteLine($"上傳文件 {objectName} 失敗。狀態(tài)碼:{response.StatusCode}");
return false;
}
}
}
catch (WebException webEx)
{
HandleWebException(webEx, "上傳");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"上傳時(shí)發(fā)生未知錯(cuò)誤:{ex.Message}");
return false;
}
}
public bool DownloadFile(string bucketName, string objectName, string savePath)
{
try
{
string url = $"{_minioEndpoint}/{bucketName}/{objectName}";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
string contentHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
SignRequest(request, bucketName, objectName, contentHash);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream responseStream = response.GetResponseStream())
using (FileStream fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write))
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
fileStream.Write(buffer, 0, bytesRead);
}
}
Console.WriteLine($"成功下載文件 {objectName} 到 {savePath}。");
return true;
}
else
{
Console.WriteLine($"下載文件 {objectName} 失敗。狀態(tài)碼:{response.StatusCode}");
return false;
}
}
}
catch (WebException webEx)
{
HandleWebException(webEx, "下載");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"下載時(shí)發(fā)生未知錯(cuò)誤:{ex.Message}");
return false;
}
}
public bool DeleteFile(string bucketName, string objectName)
{
try
{
string url = $"{_minioEndpoint}/{bucketName}/{objectName}";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "DELETE";
string contentHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
SignRequest(request, bucketName, objectName, contentHash);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine($"成功刪除文件 {objectName} 從桶 {bucketName}。");
return true;
}
else
{
Console.WriteLine($"刪除文件 {objectName} 失敗。狀態(tài)碼:{response.StatusCode}");
return false;
}
}
}
catch (WebException webEx)
{
HandleWebException(webEx, "刪除");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"刪除時(shí)發(fā)生未知錯(cuò)誤:{ex.Message}");
return false;
}
}
private void HandleWebException(WebException webEx, string operation)
{
if (webEx.Response != null)
{
using (StreamReader reader = new StreamReader(webEx.Response.GetResponseStream()))
{
string responseText = reader.ReadToEnd();
Console.WriteLine($"{operation}時(shí)發(fā)生 Web 異常:{webEx.Message}。狀態(tài)碼:{(int)((HttpWebResponse)webEx.Response).StatusCode}。響應(yīng)內(nèi)容:{responseText}");
}
}
else
{
Console.WriteLine($"{operation}時(shí)發(fā)生 Web 異常:{webEx.Message}");
}
}
private void SignRequest(HttpWebRequest request, string bucketName, string objectName, string contentHash)
{
string httpRequestMethod = request.Method;
string cleanedObjectName = objectName.StartsWith("/") ? objectName.Substring(1) : objectName;
string encodedObjectName = Uri.EscapeDataString(cleanedObjectName).Replace("%2F", "/");
string canonicalUri = $"/{bucketName}/{encodedObjectName}";
string canonicalQueryString = "";
var headersToSign = new SortedList<string, string>();
string hostHeaderValue = request.RequestUri.Host;
if (!request.RequestUri.IsDefaultPort)
{
hostHeaderValue += ":" + request.RequestUri.Port;
}
headersToSign.Add("host", hostHeaderValue);
headersToSign.Add("x-amz-content-sha256", contentHash);
DateTime requestDateTime = DateTime.UtcNow;
string amzDate = requestDateTime.ToString("yyyyMMddTHHmmssZ", CultureInfo.InvariantCulture);
headersToSign.Add("x-amz-date", amzDate);
if (request.Method == "PUT" || request.Method == "POST")
{
string actualContentType = request.ContentType;
if (string.IsNullOrEmpty(actualContentType))
{
actualContentType = "application/octet-stream";
}
headersToSign.Add("content-type", actualContentType);
}
StringBuilder canonicalHeadersBuilder = new StringBuilder();
foreach (var header in headersToSign)
{
canonicalHeadersBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}:{1}\n", header.Key, header.Value.Trim());
}
string canonicalHeaders = canonicalHeadersBuilder.ToString();
string signedHeaders = string.Join(";", headersToSign.Keys.ToArray());
string canonicalRequest = string.Format(CultureInfo.InvariantCulture,
"{0}\n{1}\n{2}\n{3}\n{4}\n{5}",
httpRequestMethod,
canonicalUri,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
contentHash);
string algorithm = "AWS4-HMAC-SHA256";
string dateStamp = requestDateTime.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
string credentialScope = string.Format(CultureInfo.InvariantCulture,
"{0}/{1}/{2}/aws4_request",
dateStamp,
_region,
_service);
string hashedCanonicalRequest = ToHex(Hash(Encoding.UTF8.GetBytes(canonicalRequest)));
string stringToSign = string.Format(CultureInfo.InvariantCulture,
"{0}\n{1}\n{2}\n{3}",
algorithm,
amzDate,
credentialScope,
hashedCanonicalRequest);
byte[] kSecret = Encoding.UTF8.GetBytes("AWS4" + _secretKey);
byte[] kDate = HmacSha256(kSecret, dateStamp);
byte[] kRegion = HmacSha256(kDate, _region);
byte[] kService = HmacSha256(kRegion, _service);
byte[] kSigning = HmacSha256(kService, "aws4_request");
byte[] signatureBytes = HmacSha256(kSigning, stringToSign);
string signature = ToHex(signatureBytes);
string authorizationHeader = string.Format(CultureInfo.InvariantCulture,
"{0} Credential={1}/{2}, SignedHeaders={3}, Signature={4}",
algorithm,
_accessKey,
credentialScope,
signedHeaders,
signature);
request.Headers["Authorization"] = authorizationHeader;
request.Headers["x-amz-date"] = amzDate;
request.Headers["x-amz-content-sha256"] = contentHash;
}
private static byte[] Hash(byte[] bytes)
{
using (SHA256 sha256 = new SHA256Managed())
{
return sha256.ComputeHash(bytes);
}
}
private static byte[] HmacSha256(byte[] key, string data)
{
using (HMACSHA256 hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
}
}
private static string ToHex(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
}
}
}
測(cè)試代碼如下:
using System;
using System.IO;
using System.Threading;
namespace MinioHttpOperatorDemo
{
class Program
{
static void Main(string[] args)
{
string minioEndpoint = "http://127.xxx.xxx.xxx:9000";
string accessKey = "accessKey ";
string secretKey = "secretKey ";
MinioHttpOperator minioOperator = new MinioHttpOperator(minioEndpoint, accessKey, secretKey);
string bucketName = "bucketName ";
string testFolder = "test";
string localFilePath1 = Path.Combine(Path.GetTempPath(), "testfile1.txt");
string localFilePath2 = Path.Combine(Path.GetTempPath(), "testfile2.jpg");
string minioObjectName1 = $"{testFolder}/document1.txt";
string minioObjectName2 = $"{testFolder}/image.jpg";
string content1 = "This is the content for document one.";
byte[] content2 = new byte[1024];
new Random().NextBytes(content2);
try
{
File.WriteAllText(localFilePath1, content1);
File.WriteAllBytes(localFilePath2, content2);
Console.WriteLine($"已在本地創(chuàng)建測(cè)試文件:{localFilePath1} 和 {localFilePath2}");
}
catch (Exception ex)
{
Console.WriteLine($"創(chuàng)建測(cè)試文件失敗:{ex.Message}");
Console.WriteLine("請(qǐng)檢查文件路徑和權(quán)限。程序?qū)⑼顺觥?);
Console.ReadKey();
return;
}
Console.WriteLine("\n--- 開(kāi)始 MinIO 批量操作 ---");
Console.WriteLine($"\n嘗試上傳文件:{localFilePath1} 到 {minioEndpoint}/{bucketName}/{minioObjectName1}...");
if (minioOperator.UploadFile(bucketName, minioObjectName1, localFilePath1, "text/plain"))
{
Console.WriteLine("文件上傳成功。");
}
else
{
Console.WriteLine("文件上傳失敗。");
}
Thread.Sleep(1000);
Console.WriteLine($"\n嘗試上傳文件:{localFilePath2} 到 {minioEndpoint}/{bucketName}/{minioObjectName2}...");
if (minioOperator.UploadFile(bucketName, minioObjectName2, localFilePath2, "image/jpeg"))
{
Console.WriteLine("文件上傳成功。");
}
else
{
Console.WriteLine("文件上傳失敗。");
}
Thread.Sleep(1000);
Console.WriteLine("\n----------------------------------------");
string downloadSavePath1 = Path.Combine(Path.GetTempPath(), "downloaded_document1.txt");
Console.WriteLine($"\n嘗試從 {minioEndpoint}/{bucketName}/{minioObjectName1} 下載文件到 {downloadSavePath1}...");
if (minioOperator.DownloadFile(bucketName, minioObjectName1, downloadSavePath1))
{
Console.WriteLine("文件下載成功。");
try
{
Console.WriteLine($"下載文件的內(nèi)容:{File.ReadAllText(downloadSavePath1)}");
}
catch (Exception ex)
{
Console.WriteLine($"讀取下載文件內(nèi)容失敗:{ex.Message}");
}
}
else
{
Console.WriteLine("文件下載失敗。");
}
Thread.Sleep(1000);
string nonExistentObject = $"{testFolder}/nonexistent.pdf";
string downloadNonExistentPath = Path.Combine(Path.GetTempPath(), "nonexistent.pdf");
Console.WriteLine($"\n嘗試下載不存在的文件:{nonExistentObject}...");
if (!minioOperator.DownloadFile(bucketName, nonExistentObject, downloadNonExistentPath))
{
Console.WriteLine("下載不存在的文件失敗(預(yù)期結(jié)果)。");
}
Thread.Sleep(1000);
Console.WriteLine("\n----------------------------------------");
Console.WriteLine($"\n嘗試從 {minioEndpoint}/{bucketName} 刪除文件 {minioObjectName1}...");
if (minioOperator.DeleteFile(bucketName, minioObjectName1))
{
Console.WriteLine("文件刪除成功。");
}
else
{
Console.WriteLine("文件刪除失敗。");
}
Thread.Sleep(1000);
Console.WriteLine($"\n嘗試從 {minioEndpoint}/{bucketName} 刪除文件 {minioObjectName2}...");
if (minioOperator.DeleteFile(bucketName, minioObjectName2))
{
Console.WriteLine("文件刪除成功。");
}
else
{
Console.WriteLine("文件刪除失敗。");
}
Console.WriteLine("\n--- MinIO 批量操作結(jié)束 ---");
try
{
if (File.Exists(localFilePath1))
{
File.Delete(localFilePath1);
Console.WriteLine($"已清理本地測(cè)試文件:{localFilePath1}");
}
if (File.Exists(localFilePath2))
{
File.Delete(localFilePath2);
Console.WriteLine($"已清理本地測(cè)試文件:{localFilePath2}");
}
if (File.Exists(downloadSavePath1))
{
File.Delete(downloadSavePath1);
Console.WriteLine($"已清理本地下載文件:{downloadSavePath1}");
}
if (File.Exists(downloadNonExistentPath))
{
File.Delete(downloadNonExistentPath);
Console.WriteLine($"已清理本地下載文件:{downloadNonExistentPath}");
}
}
catch (Exception ex)
{
Console.WriteLine($"清理本地文件失敗:{ex.Message}");
}
Console.WriteLine("\n按任意鍵退出程序。");
Console.ReadKey();
}
}
}
轉(zhuǎn)自https://www.cnblogs.com/hanfan/p/19010378
該文章在 2025/8/1 9:41:45 編輯過(guò)