CentOS7安裝MinIO教程,C#使用MinIO看這一篇就夠了(WPF)

MinIO的詳細介紹能夠參考官網(https://min.io/product/overview)。html

簡單來講它是一個實現了AWS S3標準的100%開源的,可商用的( Apache V2 license),高性能的分佈式對象存儲管理系統。linux

AWS S3是什麼(https://zhuanlan.zhihu.com/p/112057573):web

  1. 提供了統一的接口 REST/SOAP 來統一訪問任何數據
  2. 對 S3 來講,存在裏面的數據就是對象名(鍵),和數據(值)
  3. 不限量,單個文件最高可達 5TB
  4. 高速。每一個 bucket 下每秒可達 3500 PUT/COPY/POST/DELETE 或 5500 GET/HEAD 請求
  5. 具有版本,權限控制能力
  6. 具有數據生命週期管理能力

此次主要介紹CentOS7如何安裝MinIO,以及C#如何使用MinIO的.NET SDK來上傳文件(顯示上傳進度)。express

首先安裝好CentOS7 64位操做系統,而後在/usr/local下新建一個minio目錄,在minio目錄下新建三個子文件夾,分別是:json

bin 用來保存minio的主程序vim

data 用來保存用戶上傳的文件centos

etc 用來保存minio的配置文件服務器

而後新建MinIO運行所需的系統組和用戶,並給/usr/local/minio/分配組信息app

groupadd -g 2020 minio
useradd -r -M -u 2020 -g 2020 -c "Minio User" -s /sbin/nologin  minio
chown -R minio:minio /usr/local/minio

創建了一個組ID是2020,名稱是minio的系統組,而後添加了一個不能登陸的名稱是minio,所屬組ID是2020而且我的ID也是2020的系統用戶。async

MinIO默認提供的http訪問端口是9000,咱們提早在防火牆裏把9000端口打開

firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --reload

同時咱們須要給MinIO掛載要給存儲空間比較大的磁盤用來存儲文件

執行lvdisplay找到比較大的分區,掛載給/usr/local/minio目錄

運行

mount /dev/centos/home /usr/local/minio

把這個200多G的邏輯卷掛載給minio的目錄。

同時爲了重啓後自動掛載,咱們須要執行如下代碼

vim /etc/fstab

把centos-home這句換成如下內容,保存退出,就可讓系統重啓後自動掛載空間到/usr/local/minio目錄了

/dev/mapper/centos-home /usr/local/minio        xfs     defaults        0 0

 

而後咱們運行lsblk就能夠看到磁盤空間正確掛載在minio目錄下了

 從官網(或者經過下載工具下載到本地後上傳)下載(https://min.io/download#/linux)到CentOS的/usr/local/minio/bin目錄下

其中第二句(chmod +x minio)等把minio文件移動到/usr/local/minio/bin後在執行

第三句先不要執行 

下面咱們創建MinIO的配置文件

vim /usr/local/minio/etc/minio.conf

寫入如下內容(--address後面的IP 是CentOS的IP地址)

MINIO_VOLUMES="/usr/local/minio/data"
MINIO_OPTS="-C /usr/local/minio/etc --address 192.168.127.131:9000

創建MinIO的service文件,讓它隨系統啓動而啓動

vim /etc/systemd/system/minio.service 

寫入如下內容

[Unit]
Description=MinIO
Documentation=https://docs.min.io
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/usr/local/minio/bin/minio

[Service]
# User and group
User=minio
Group=minio

EnvironmentFile=/usr/local/minio/etc/minio.conf
ExecStart=/usr/local/minio/bin/minio server $MINIO_OPTS $MINIO_VOLUMES

# Let systemd restart this service always
Restart=always

# Specifies the maximum file descriptor number that can be opened by this process
LimitNOFILE=65536

# Disable timeout logic and wait until process is stopped
TimeoutStopSec=infinity
SendSIGKILL=no

[Install]
WantedBy=multi-user.target

而後咱們啓動MinIO

systemctl enable minio.service
systemctl start minio.service
systemctl status minio.service

啓動正常後,咱們訪問http://192.168.127.131:9000就應該能夠看到web管理端的登陸界面

 咱們能夠經過下面的命令來獲取到默認的登陸名和密碼(minioadmin)

cat /usr/local/minio/data/.minio.sys/config/config.json

 

若是要自定義登陸名和密碼,能夠在/usr/local/minio/etc/minio.conf中增長兩個配置內容

MINIO_ACCESS_KEY="登陸名"
MINIO_SECRET_KEY="登陸密碼"

至此,MinIO的安裝就介紹完畢,下面是本文重點,C#中使用WPF客戶端如何獲取文件上傳時的進度。

首先新建一個項目MinIO,項目類型是WPF,項目的.NET版本是4.6

引用官方的Minio .NET SDK以及其它一些須要的類庫

在項目的根目錄添加一個nlog.config並設置爲較新時複製,內容以下(主要用來作一些日誌記錄):

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Info">
    <targets>
        <default-target-parameters xsi:type="File" createDirs="true" keepFileOpen="true" autoFlush="false" openFileFlushTimeout="10" openFileCacheTimeout="30" archiveAboveSize="10240" archiveNumbering="Sequence" concurrentWrites="true" encoding="UTF-8"/>
        <target xsi:type="File" name="InfoFile" fileName="${basedir}/InfoLogs/log.txt" archiveFileName="${basedir}/InfoLogs/log.{#}.txt">
            <layout xsi:type="JsonLayout">
                <attribute name="counter" layout="${counter}" />
                <attribute name="time" layout="${longdate}" />
                <attribute name="level" layout="${level:upperCase=true}"/>
                <attribute name="message" layout="${message:format=message}" encode="false" />
            </layout>
        </target>
        <target xsi:type="File" name="ErrorFile" fileName="${basedir}/ErrorLogs/log.txt" archiveFileName="${basedir}/ErrorLogs/log.{#}.txt">
            <layout xsi:type="JsonLayout">
                <attribute name="time" layout="${longdate}" />
                <attribute name="level" layout="${level:upperCase=true}"/>
                <attribute name="message" layout="${message}" encode="false" />
                <attribute name="exception">
                    <layout xsi:type="JsonLayout">
                        <attribute name="callsite" layout="${callsite}" />
                        <attribute name="callsite-linenumber" layout="${callsite-linenumber} " />
                    </layout>
                </attribute>
            </layout>
        </target>
    </targets>
    <rules>
        <logger name="*" minlevel="Info" writeTo="InfoFile" />
        <logger name="*" minlevel="Error" writeTo="ErrorFile" />
    </rules>
</nlog>

在使用過程當中發現MinIO的.NET SDK中並無回顯上傳進度的地方(若是有請告知),通過一些摸索,在它暴露的日誌記錄的地方獲取到當前上傳文件塊的編號,經過MVVM的Messenger傳遞給主程序,主程序接收後計算出當前進度並顯示出來。

咱們在App.xaml中添加一個啓動事件App_OnStartup,在啓動的時候初始化一下Nlog,並定義一下MinIO上傳時遇到大文件分塊的默認值(5MB)

namespace MinIO
{
    /// <summary>
    /// App.xaml 的交互邏輯
    /// </summary>
    public partial class App : Application
    {
        public static NLog.Logger NewNLog;
        public static long MinimumPartSize = 5 * 1024L * 1024L;//單次上傳文件請求最大5MB

        private void App_OnStartup(object sender, StartupEventArgs e)
        {
            NewNLog = NLog.LogManager.GetLogger("MinIOLoger");
        }
    }
}

 在MainWindow.xaml中咱們添加一個按鈕用來上傳文件,一個TextBlock用來顯示上傳文件信息和進度

<Window x:Class="MinIO.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MinIO"
        mc:Ignorable="d"
        DataContext="{Binding FileUploadViewModel,Source={StaticResource Locator}}"
        Title="MainWindow" Height="450" Width="800" Loaded="MainWindow_OnLoaded">
    <Grid>
        <Button Content="上傳文件" HorizontalAlignment="Left" Margin="47,51,0,0" VerticalAlignment="Top" Width="75" Click="ButtonUpload_Click"/>
        <TextBlock HorizontalAlignment="Left" Margin="47,158,0,0" TextWrapping="Wrap" VerticalAlignment="Top">
            <Run Text=" 文件名:"></Run>
            <Run Text="{Binding Path=FileName}"></Run>
            <Run Text="&#13;&#10;"></Run>

            <Run Text="文件大小:"></Run>
            <Run Text="{Binding Path=FileSize}"></Run>
            <Run Text="&#13;&#10;"></Run>

            <Run Text="上傳進度:"></Run>
            <Run Text="{Binding Path=UploadProcess}"></Run>

        </TextBlock>

    </Grid>
</Window>

同時咱們定義一個ViewModel來實現文件上傳信息變化時的通知(ViewModel目錄中新建一個FileUploadViewModel.cs)

using System.ComponentModel;

namespace MinIO.ViewModel
{
    public class FileUploadViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _filename { get; set; }
        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName
        {
            get => _filename;
            set
            {
                _filename = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FileName"));
            }
        }

        private long _fileSize { get; set; }
        /// <summary>
        /// 文件大小
        /// </summary>
        public long FileSize
        {
            get => _fileSize;
            set
            {
                _fileSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FileSize"));
            }
        }

        private long _partNumber { get; set; }
        /// <summary>
        /// 當前上傳塊
        /// </summary>
        public long PartNumber
        {
            get => _partNumber;
            set
            {
                _partNumber = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PartNumber"));
            }
        }

        private long _totalParts { get; set; }
        /// <summary>
        /// 文件所有塊數
        /// </summary>
        public long TotalParts
        {
            get => _totalParts;
            set
            {
                _totalParts = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TotalParts"));
            }
        }

        private string _uploadProcess { get; set; }
        /// <summary>
        /// 上傳進度展現
        /// </summary>
        public string UploadProcess
        {
            get => _uploadProcess;
            set
            {
                _uploadProcess = value;
                PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("UploadProcess"));
            }
        }
    }
}

同時爲了能讓MinIO根據不一樣的文件類型展現不一樣的圖標,咱們還須要創建一個ContentType對應關係類(ContentTypeHelper.cs)

using System.Collections.Generic;

namespace MinIO.Helper
{
    public static class ContentTypeHelper
    {
        private static readonly Dictionary<string,string> DictionaryContentType=new Dictionary<string, string>
        {
            {"default","application/octet-stream"},
            {"bmp","application/x-bmp"},
            {"doc","application/msword"},
            {"docx","application/msword"},
            {"exe","application/x-msdownload"},
            {"gif","image/gif"},
            {"html","text/html"},
            {"jpg","image/jpeg"},
            {"mp4","video/mpeg4"},
            {"mpeg","video/mpg"},
            {"mpg","video/mpg"},
            {"ppt","application/x-ppt"},
            {"pptx","application/x-ppt"},
            {"png","image/png"},
            {"rar","application/zip"},
            {"txt","text/plain"},
            {"xls","application/x-xls"},
            {"xlsx","application/x-xls"},
            {"zip","application/zip"},
        };
        /// <summary>
        /// 根據文件擴展名(不含.)返回ContentType
        /// </summary>
        /// <param name="fileExtension">文件擴展名(不包含.)</param>
        /// <returns></returns>
        public static string GetContentType(string fileExtension)
        {
            return DictionaryContentType.ContainsKey(fileExtension) ? DictionaryContentType[fileExtension] : DictionaryContentType["default"];
        }        
    }
}

咱們須要新建一個實現MinIO中IRequestLogger接口的類(LogHelper.cs),用來接收日誌信息經過處理日誌信息,來計算出當前上傳進度信息

using GalaSoft.MvvmLight.Messaging;
using Minio;
using Minio.DataModel.Tracing;
using System.Net;

namespace MinIO.Helper
{
    public class LogHelper : IRequestLogger
    {
        public void LogRequest(RequestToLog requestToLog, ResponseToLog responseToLog, double durationMs)
        {
            if (responseToLog.statusCode == HttpStatusCode.OK)
            {
                foreach (var header in requestToLog.parameters)
                {
                    if (!string.Equals(header.name, "partNumber")) continue;
                    if(header.value==null) continue;
                    int.TryParse(header.value.ToString(), out var partNumber);//minio遇到上傳文件大於5MB時,會進行分塊傳輸,這裏就是當前塊的編號(遞增)
                    Messenger.Default.Send(partNumber, "process");//發送給主界面計算上傳進度
                    break;
                }
            }
        }
    }
}

在MainWindow初始化的時候初始化MinIO的配置信息,同時用MVVM的Messenger來接收當前上傳的塊編號,用來計算上傳進度。

using GalaSoft.MvvmLight.Messaging;
using Microsoft.Win32;
using Minio;
using Minio.Exceptions;
using MinIO.Helper;
using MinIO.ViewModel;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;

namespace MinIO
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        private static string _endpoint = "192.168.127.131:9000";//minio服務器地址
        private static string _accessKey = "minioadmin";//受權登陸帳號
        private static string _secretKey = "minioadmin";//受權登陸密碼
        private static MinioClient _minioClient;

        public MainWindow()
        {
            InitializeComponent();
        }
        private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
        {
            _minioClient = new MinioClient(_endpoint, _accessKey, _secretKey);
            Messenger.Default.Register<int>(this, "process", obj =>
             {
                 try
                 {
                     Debug.WriteLine($"當前塊編號:{obj}");
                     if (obj == 0)
                     {
                         ViewModelLocator.Instance.FileUploadViewModel.UploadProcess = "0.00%";
                         return;
                     }
                     ViewModelLocator.Instance.FileUploadViewModel.PartNumber = obj; 
                     ViewModelLocator.Instance.FileUploadViewModel.UploadProcess =
                         $"{(float)ViewModelLocator.Instance.FileUploadViewModel.PartNumber / ViewModelLocator.Instance.FileUploadViewModel.TotalParts:P2}";//計算文件上傳進度
                 }
                 catch (Exception exception)
                 {
                     App.NewNLog.Error($"計算上傳進度時出錯:{exception}");
                 }
             });
        }
        private void ButtonUpload_Click(object sender, RoutedEventArgs e)
        {
            var open = new OpenFileDialog
            {
                CheckFileExists = true,
                CheckPathExists = true,
            };
            if (open.ShowDialog(this) == false)
            {
                return;
            }

            ViewModelLocator.Instance.FileUploadViewModel.FileName = open.SafeFileName;
            try
            {
                Dispatcher?.InvokeAsync(async () =>
                {
                    await Run(_minioClient, "test", open.FileName, ViewModelLocator.Instance.FileUploadViewModel.FileName);
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private static async Task Run(MinioClient minio, string userBucketName, string uploadFilePath, string saveFileName)
        {
            var bucketName = userBucketName;
            var location = "us-east-1";
            var objectName = saveFileName;
            var filePath = uploadFilePath;
            var contentType = ContentTypeHelper.GetContentType(saveFileName.Substring(saveFileName.LastIndexOf('.') + 1));
            var file = new FileInfo(uploadFilePath);

            try
            {
                var found = await minio.BucketExistsAsync(bucketName);
                if (!found)
                {
                    await minio.MakeBucketAsync(bucketName, location);
                }

                _minioClient.SetTraceOn(new LogHelper());//咱們在上傳開始的時候,打開日誌,經過日誌拋出的塊編號來計算出當前進度

                ViewModelLocator.Instance.FileUploadViewModel.FileSize = file.Length;
                ViewModelLocator.Instance.FileUploadViewModel.TotalParts = file.Length / App.MinimumPartSize + 1;//計算出文件總塊數

                await minio.PutObjectAsync(bucketName, objectName, filePath, contentType);//上傳文件
                Debug.WriteLine("Successfully uploaded " + objectName);

            }
            catch (MinioException e)
            {
                App.NewNLog.Error($"File Upload Error: {e}");
                Debug.WriteLine($"File Upload Error: {e.Message}");
            }
            finally
            {
                _minioClient.SetTraceOff();
            }
        }
    }
}

具體效果以下:

 

至此,咱們實現了CentOS7下安裝MinIO,而且在C#客戶端下展現文件上傳進度的功能。

PS:代碼還不完善,少了一個小於5M的時候,直接展現上傳成功的代碼。

相關文章
相關標籤/搜索