-
Notifications
You must be signed in to change notification settings - Fork 247
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #823 from neozhu/feature/minio
Integrate MinIO for File Uploads
- Loading branch information
Showing
17 changed files
with
229 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,10 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using SixLabors.ImageSharp.Processing; | ||
|
||
namespace CleanArchitecture.Blazor.Application.Common.Interfaces; | ||
|
||
public interface IUploadService | ||
{ | ||
Task<string> UploadAsync(UploadRequest request); | ||
void Remove(string filename); | ||
|
||
Task<string> UploadImageAsync(Stream imageStream, UploadType uploadType, ResizeOptions? resizeOptions = null, string? fileName=null); | ||
Task RemoveAsync(string filename); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,26 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using SixLabors.ImageSharp.Processing; | ||
|
||
namespace CleanArchitecture.Blazor.Application.Common.Models; | ||
|
||
public class UploadRequest | ||
{ | ||
public UploadRequest(string fileName, UploadType uploadType, byte[] data, bool overwrite = false) | ||
public UploadRequest(string fileName, UploadType uploadType, byte[] data, bool overwrite = false,string? folder=null, ResizeOptions? resizeOptions=null) | ||
{ | ||
FileName = fileName; | ||
UploadType = uploadType; | ||
Data = data; | ||
Overwrite = overwrite; | ||
Folder = folder; | ||
ResizeOptions = resizeOptions; | ||
} | ||
|
||
public string FileName { get; set; } | ||
public string? Extension { get; set; } | ||
public UploadType UploadType { get; set; } | ||
public bool Overwrite { get; set; } | ||
public byte[] Data { get; set; } | ||
public string? Folder { get; set; } | ||
public ResizeOptions? ResizeOptions { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CleanArchitecture.Blazor.Infrastructure.Configurations; | ||
|
||
public class MinioOptions | ||
{ | ||
public const string Key = "Minio"; | ||
public string Endpoint { get; set; } = "https://minio.blazors.app:8843"; | ||
public string AccessKey { get; set; } = string.Empty; | ||
public string SecretKey { get; set; } = string.Empty; | ||
public string BucketName { get; set; } = "files"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
using CleanArchitecture.Blazor.Application.Common.Extensions; | ||
using CleanArchitecture.Blazor.Infrastructure.Configurations; | ||
using Microsoft.AspNetCore.StaticFiles; | ||
using Minio; | ||
using Minio.DataModel.Args; | ||
using Minio.Exceptions; | ||
using SixLabors.ImageSharp; | ||
using SixLabors.ImageSharp.Formats.Png; | ||
using SixLabors.ImageSharp.Processing; | ||
|
||
namespace CleanArchitecture.Blazor.Infrastructure.Services; | ||
public class MinioUploadService : IUploadService | ||
{ | ||
private readonly IMinioClient _minioClient; | ||
private readonly string _bucketName; | ||
private readonly string _endpoint; | ||
public MinioUploadService(MinioOptions options) | ||
{ | ||
var opt = options; | ||
_endpoint = opt.Endpoint; | ||
_minioClient = new MinioClient() | ||
.WithEndpoint(_endpoint) | ||
.WithCredentials(opt.AccessKey, opt.SecretKey) | ||
.WithSSL() | ||
.Build(); | ||
_bucketName = opt.BucketName; | ||
} | ||
|
||
public async Task<string> UploadAsync(UploadRequest request) | ||
{ | ||
// Use FileExtensionContentTypeProvider to determine the MIME type. | ||
var provider = new FileExtensionContentTypeProvider(); | ||
if (!provider.TryGetContentType(request.FileName, out var contentType)) | ||
{ | ||
contentType = "application/octet-stream"; | ||
} | ||
|
||
// Define common bitmap image extensions (not including vector formats like SVG). | ||
var bitmapImageExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp" }; | ||
var ext = Path.GetExtension(request.FileName).ToLowerInvariant(); | ||
|
||
// If ResizeOptions is provided and the file is a bitmap image, process the image. | ||
if (request.ResizeOptions != null && Array.Exists(bitmapImageExtensions, e => e.Equals(ext, StringComparison.OrdinalIgnoreCase))) | ||
{ | ||
using var inputStream = new MemoryStream(request.Data); | ||
using var outputStream = new MemoryStream(); | ||
using var image = Image.Load(inputStream); | ||
image.Mutate(x => x.Resize(request.ResizeOptions)); | ||
// Convert the image to PNG format. | ||
image.Save(outputStream, new PngEncoder()); | ||
request.Data = outputStream.ToArray(); | ||
contentType = "image/png"; | ||
} | ||
|
||
// Ensure the bucket exists. | ||
bool bucketExists = await _minioClient.BucketExistsAsync(new BucketExistsArgs().WithBucket(_bucketName)); | ||
if (!bucketExists) | ||
{ | ||
await _minioClient.MakeBucketAsync(new MakeBucketArgs().WithBucket(_bucketName)); | ||
} | ||
|
||
// Build folder path based on UploadType and optional Folder property. | ||
string folderPath = $"{request.UploadType.GetDescription()}"; | ||
if (!string.IsNullOrWhiteSpace(request.Folder)) | ||
{ | ||
folderPath = $"{folderPath}/{request.Folder.Trim('/')}"; | ||
} | ||
|
||
// Construct the object name including the folder path. | ||
string objectName = $"{folderPath}/{request.FileName}"; | ||
|
||
using (var stream = new MemoryStream(request.Data)) | ||
{ | ||
await _minioClient.PutObjectAsync(new PutObjectArgs() | ||
.WithBucket(_bucketName) | ||
.WithObject(objectName) | ||
.WithStreamData(stream) | ||
.WithObjectSize(stream.Length) | ||
.WithContentType(contentType) | ||
); | ||
} | ||
|
||
// Return the URL constructed using the configured Endpoint. | ||
return $"https://{_endpoint}/{_bucketName}/{objectName}"; | ||
} | ||
public async Task RemoveAsync(string filename) | ||
{ | ||
// Remove the "https://" or "http://" prefix from the URL and extract the bucket and object name. | ||
Uri fileUri = new Uri(filename); | ||
|
||
// Ensure the URL is well-formed and can be parsed | ||
if (!fileUri.IsAbsoluteUri) | ||
throw new ArgumentException("Invalid URL format."); | ||
|
||
// Extract the bucket from the path portion of the URL | ||
string[] pathParts = fileUri.AbsolutePath.TrimStart('/').Split('/', 2); | ||
if (pathParts.Length < 2) | ||
throw new ArgumentException("URL format must be 'https://<endpoint>/<bucket>/<object>'."); | ||
|
||
string bucket = pathParts[0]; | ||
string objectName = pathParts[1]; | ||
|
||
try | ||
{ | ||
// Proceed to remove the object from the correct bucket | ||
await _minioClient.RemoveObjectAsync(new RemoveObjectArgs() | ||
.WithBucket(bucket) | ||
.WithObject(objectName)); | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new Exception("Error deleting object", ex); | ||
} | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.