MinIO Nedir? MinIO Sunucusu Nasıl Kurulur?
MinIO; çok basit anlamda bir nesne depolama aracıdır. Kullananlar bilir, Amazon Web Services içerisindeki S3 aracıyla neredeyse aynı işlevi görmektedir. Ancak S3 araçlarının fiyatları ve özellikle finans sektöründeki regülasyonlar göz önüne alındığında, verilerin yurtdışında saklanabilmesi yasal olmadığından S3 gibi bulut tabanlı sistemleri kullanmak istemeyebilirsiniz. Bu noktada MinIO ile sunucularınızı oluşturup kullanabilirsiniz.
Ben bu araç ile çalıştığım projenin bir geliştirme esnasında tanıştım. MinIO öncesinde projelerimizde ki dosya yükleme aşamalarında FileHeaderControl helper’ları yazarak dosyanın uzantısından, metadatasındaki byte dizisine kadar kontrol ederek içeriye alıyorduk. Bu yöntem şimdiye kadar yapılan tüm saldırılarda hiç bir zaafiyet yaşatmasa da yüklenen dosyaların projelerimizle aynı sunucuda olmaları aslında bizi rahatsız edebilen bir detaydı.
Proje kapsamında sunuculardan bir tanesini yalnızca intranet erişebilecek şekilde object storage sunucusu olmak üzere ayırdık. Ardından MinIO Sunucularını bu sisteme kurduktan sonra artık dosya yükleme ve indirme işlemlerimize oradan devam ediyoruz.
MinIO Altyapısı Nasıl Kurulur?
MinIO Sunucularınızı ister Windows, ister MacOS isterseniz de Docker Container üzerinde kurulumunu yapabilirsiniz. Ben bu yazımda sizlere MacOS sistemi üzerinde kurulumun nasıl yapıldığını aktaracağım. Ancak MacOS dışındaki işletim sistemlerinde çalışan okuyucular bu adresten kendi sistemlerine nasıl kurulum yapıldığını inceleyebilirler.
MacOS için her şeyden önce sisteminizde Homebrew paket yöneticisinin olması gerekmektedir. Eğer sisteminizde homebrew yüklü olup olmadığını bilmiyorsanız terminal üzerinden brew komutunu çalıştırıp anlayabilirsiniz. Sisteminizde kurulu değilse de bu site üzerinden kolayca Homebrew kurulumunuzu yapabilirsiniz.
Eğer tüm altyapımız hazırsa terminal üzerinden aşağıdaki komutları sırayla çalıştırıp MinIO aracını sistemimize kuralım.
brew install minio/stable/minio
İşte bu kadar! Artık sistemimize MinIO altyapısı kuruldu.
MinIO Sunucuları Nasıl Başlatılır?
Artık dilediğimiz kadar MinIO Sunucusunu sistemimizde başlatabiliriz. Bunun için terminalde minio komutlarını kullanacağız.
minio server *
MinIO sunucularımızı başlatırken yukardaki temel komut satırı ile işlemlerimize başlarız. Ardından sunucunun gerekli konfigürasyon ayarları yapılabilir.
Örneğin MinIO Sunucumuzun dosya yolunu belirlemek için aşağıdaki komutu terminalinizde çalıştırmanız yeterlidir.
minio server /data
Bu komutun ardından MinIO sunucusu bilgisayarınızın en üst dizininde data isminde bir klasör oluşturup burada çalışmaya başlayacaktır.
Artık çalışan bir MinIO sunucunuz var! Sunucunuza local olarak terminalde belirtildiği gibi 127.0.0.1:9000 portu üzerinden erişim sağlayabilirsiniz.
Terminal üzerinden belirtilen AccessKey ve SecretKey bilgilerinizi girerek sunucunuza giriş yapabilirsiniz.
Buradan sunucunuz için bucketlar oluşturabilir veya manuel olarak dosya yüklemelerinizi yapabilirsiniz. Bir bucket oluşturup içerisine de bir dosya yükleyelim.
Bu yüklemerler bilgisayarınız üzerinde aslında aşağıdaki Finder yolu üzerinde saklanmaktadır.
/data/medium/0 (1).jpeg
Yani aslında bucket isimlerimiz bizim klasör isimlerimiz haline gelmektedir. Bir bucket daha oluşturduğumuzda data klasörü altında ilgili bucket isminde bir klasör daha oluşacaktır.
.NET Core Projelerinde Nasıl Kullanılır?
Birlikte bir .NET Core web projesi oluşturalım ve tasarım kısmına çok takılmadan küçük bir dosya yükleme ve indirme yapısı oluşturalım.
MinIO Paketinin Yüklenmesi
Projemizi oluşturduktan sonra NuGet Package Manager üzerinden MinIO paketini projeye dahil ediniz.
Ardından bu projemizde kullanmak üzere bir ViewModel oluşturalım ve ismine FileViewModel verelim.
Bu ViewModel nesnemizi dosya yükleme sayfamızın post metodunda kullanacağız. Dosya yükleme sayfamızın tasarımını ise basitçe aşağıdaki gibi yapabiliriz:
@model FileViewModel; <div class="container" style="margin-top:100px"> <br/> @{ if(Model != null) { <a href="@Url.Action("DownloadFile", new { fileName = Model.FileName })">@Model.FileName indir</a> } } <div class="row"> <h1>MinIO Dosya Yükleme Örneği</h1><br/> <form method="post" enctype="multipart/form-data" asp-controller="Home" asp-action="Index"> <div class="form-group"> <div class="col-md-10"> <input type="file" name="file"> <br/> <input class="btn btn-success" type="submit" value="Upload"> </div> </div> </form> </div> </div>
Ardından controller yapımızı ise 3 actiondan oluşacak şekilde tasarlayalım:
public class HomeController : Controller { public IActionResult Index() { return View(); } [HttpPost] public IActionResult Index(IFormFile file) { return View(); } public IActionResult DownloadFile(string fileName) { return View(); } }
Projemizin altyapısını hazırladık. Şimdi biraz daha derinlerine inip MinIO ile nasıl dosya yüklemesi yapılacağını görelim.
Öncelikle appsettings dosyamıza MinIO ile alakalı erişim bilgilerini ekleyebiliriz:
Konfigürasyon bilgilerine erişebilmemiz için HomeController sınıfı içerisinde bir constructor oluşturup IConfiguration interface’ini kullanmamız gerekecektir.
MinIO İle Dosya Yükleme
Şimdi ise Index’in Post metodunda MinIO sunucumuza bağlantı kurup yeni bir MinioClient nesnesi oluşturabiliriz. MinioClient nesneleri ile MinIO Sunucularımıza dosya yükleme veya dosya indirme işlemlerini yapabiliriz.
MinIO kullanırken sizlere tavsiyem dosya yüklemelerinizi dosya yolundan değilde MemoryStream ‘ler kullanarak dosyaları yüklemeniz olacaktır. Projemizde yaptığımız sistem denemelerinde MinIO üzerinde bu yolla yükleme yapmak çok daha hızlı sonuç vermektedir.
MinIO Dosya yükleme metodları hakkında MinIO ‘nun kendi dokümantasyonunu referans alarak şunu söyleyebilirim: her zaman try.. catch içerisinde yüklemelerinizi yapmalısınız. Çünkü MinIO dosyanızın yüklenip yüklenemediği hakkında bir boolean değer döndürmemektedir, bir exception fırlatmaktadır.
MinIO ‘nun tüm metodları asenkron çalışmaktadır ve bu sebeple Index sayfamızın Post metodu da async ve Task içerisinde yazılmalıdır.
Dosya yüklerken kullanılan metodumuz PutObjectAsync. Bu metod içerisinde dikkat edilmesi gereken bazı noktalar var. Örneğin objectName özelliğini kullanırken dosya ismimizin mutlaka dosya uzantısı ile birlikte girilmesi gerekmektedir. Ayrıca dosya yüklemelerinde kullanılan bu metodu biraz incelediğimizde dosyalarımızı şifreli bir biçimde saklayabileceğimiz de görülmektedir. (Bu şekilde dosya yükleme işlemini ilerleyen blog yazılarımda ayrıca ele alabilirim. Şifreli saklanan dosyalarımızı doğrudan dosya yoluna gidip açmak istediğinizde hata ile karşılaşılmaktadır. MinIO; şifrelenerek yüklenen bu dosyaların yalnızca kendisi üzerinden erişilebilir olmasını sağlamış.)
[HttpPost] public async Task<IActionResult> Index(IFormFile file) { FileViewModel viewModel = new FileViewModel(); //Yüklenen dosyanın MemoryStream nesnesini oluşturalım MemoryStream stream = new MemoryStream(); file.CopyTo(stream); stream.Position = 0; //MinIO Bağlantısı string endPoint = _config["Minio:Endpoint"]; string accessKey = _config["Minio:Accesskey"]; string secretKey = _config["Minio:SecretKey"]; MinioClient minioClient = new MinioClient(endPoint, accessKey, secretKey); string bucketName = "medium"; string objectName = Guid.NewGuid().ToString().Substring(0,7) + Path.GetExtension(file.FileName); string contentType = file.ContentType; try { //Bu isimde bir bucket olup olmadığını kontrol edelim. bool isFound = await minioClient.BucketExistsAsync(bucketName); if (!isFound) { //Bu isimde bir bucket yoksa oluşturalım. await minioClient.MakeBucketAsync(bucketName); } await minioClient.PutObjectAsync(bucketName, objectName, stream, stream.Length, contentType); viewModel.FileName = objectName; } catch(MinioException m) { viewModel.Message = m.message; } return View(viewModel); }
İşte MinIO kullanılarak bir dosya yüklemesi işlemini halletmiş olduk!
MinIO İle Dosya İndirme
Dosyamızı MinIO üzerinden indirmek de yüklemek kadar oldukça basittir. Ancak burada da dikkat edilmesi gereken bir özellik bulunmaktadır. Dosyamızın content type özelliği. Bu özelliğe erişebilmek için dosyamızın uzantısından faydalanacağız. Öncelikle HomeController sınıfımız içerisinde bir GetContentType metodu oluşturalım. Bu metod genellikle dosya yüklemelerinde kullanılan dosya uzantılarının content type değerini bize döndüren basit bir metottur:
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 { return "application/octet-stream"; } }
MinIO Üzerinden dosya indirirken kullanılan metod ise GetObjectAsyncmetodudur. Bu metod içerisine bucket ismi ve obje ismi değişkenlerini aldıktan sonra sizlere bir Stream nesnesi döndüren fonksiyon sunar. Daha öncesinde oluşturduğumuz yeni Stream nesnesine MinIO üzerinden dönen stream nesnesinin CopyTo metoduyla kopyalayabiliriz.
Dosya indirme action metodumuz ise aşağıdaki gibi olacaktır:
public async Task<FileResult> DownloadFile(string fileName) { //MinIO Bağlantısı string endPoint = _config["Minio:Endpoint"]; string accessKey = _config["Minio:Accesskey"]; string secretKey = _config["Minio:SecretKey"]; MinioClient minioClient = new MinioClient(endPoint, accessKey, secretKey); string bucketName = "medium"; MemoryStream memoryStream = new MemoryStream(); try { //Eğer ilgili bucket altında ismi verilen object yer almıyorsa bu metod bize hata fırlatacaktır. await minioClient.StatObjectAsync(bucketName, fileName); await minioClient.GetObjectAsync(bucketName, fileName, (stream) => { stream.CopyTo(memoryStream); }); memoryStream.Position = 0; } catch { } return File(memoryStream, GetContentType(fileName), fileName); }
Bu şekilde dosya yükleme işlemlerini zaafiyetlere karşı daha güvenli hale getirdiğimize inanıyorum. Ayrıca Stream nesneleri üzerinden haberleştiğimiz için yüklenen dosyaları siteniz üzerinde göstermek isterseniz byte dizisine çevirip Base64 ile gösterebilirsiniz.
MinIO’ya ait kaynak kodlarına göz atmak isterseniz GitHub profilimdeki bu adresi inceleyebilirsiniz.