MinIO的簡單使用實踐


MinIO是一款高性能高可用的文件系統服務,可以用來替換FastDFS,這里介紹一下簡單安裝與使用。部署方式采用單機單硬盤模式。

安裝與啟動

wget https://dl.minio.io/server/minio/release/linux-amd64/minio
nuhup /root/minio server /data > /data/minio/minio.log 2>&1 &

通過環境變量可以修改控制台管理員密碼

export MINIO_ACCESS_KEY=root
export MINIO_SECRET_KEY=root_1234

控制台:http://127.0.0.1:9000

幾個概念:

  • bucket minio里邊object存儲位置的最上一級目錄,object要存儲都要指定一個bucket
  • objectName 如果只指定"文件名.擴展名"則直接存放在bucket目錄下,但還可以帶路徑、比如"/abc/文件名.擴展名"則會存放在bucket/abc/目錄下
  • 一般客戶端另外配置用戶而不是直接使用控制台管理員賬戶,使用控制台創建用戶賬號並分配權限(讀、寫、控制台等)即可。

與spring搭配開發

簡單實現文件的上傳、下載、預覽等功能。
引入依賴

implementation group: 'io.minio', name: 'minio', version: '7.1.0'

配置類與工具類:

@Configuration
public class MinioConfig {
	
	@Value("${minio.ip}")
	private String ip;
	
	@Value("${minio.port}")
	private int port;
	
	@Value("${minio.access-key}")
	private String accessKey;
	
	@Value("${minio.secret-key}")
	private String secretKey;
	
	@Bean
	public MinioClient minioClient() {
		return MinioClient.builder()
                          .endpoint(ip, port, false) //https or not
                          .credentials(accessKey, secretKey)
                          .build();
	}
}
@Component
public class MinioUtil {
	private Logger logger = LoggerFactory.getLogger(MinioUtil.class);
	
	@Autowired
	private MinioClient minioClient;
	
	/**
	 * 創建bucket
	 * */
	private void createBucket(String bucketName) throws Exception {
		BucketExistsArgs existsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
		if(!minioClient.bucketExists(existsArgs)) {
			MakeBucketArgs makeArgs = MakeBucketArgs.builder().bucket(bucketName).build();
			minioClient.makeBucket(makeArgs);
			logger.info("bucket {} 不存在, 自動創建該bucket", bucketName);
		}
	}
	
	/**
	 * 從給定輸入流中傳輸對象並放入bucket
	 * */
	public ObjectWriteResponse putObject(String bucketName, String objectName, InputStream stream, long objectSize, String contentType) throws Exception {
        if (StringUtils.isEmpty(bucketName)) {
            throw new RuntimeException("保存的bucketName為空");
        }
        createBucket(bucketName);
        //long objSize = -1;
        long partSize = -1; //objectSize已知,partSize設為-1意為自動設置
        PutObjectArgs putArgs = PutObjectArgs.builder()
        				.bucket(bucketName)
        				.object(objectName)
        				.stream(stream, objectSize, partSize)
        				.contentType(contentType)
        				.build();
        ObjectWriteResponse response = minioClient.putObject(putArgs);
        
        return response;
	}
	
	/**
	 * 從bucket獲取指定對象的輸入流,后續可使用輸入流讀取對象
	 * getObject與minio server連接默認保持5分鍾,
	 * 每隔15s由minio server向客戶端發送keep-alive check,5分鍾后由客戶端主動發起關閉連接
	 * */
	public InputStream getObject(String bucketName, String objectName) throws Exception{
		GetObjectArgs args = GetObjectArgs.builder()
						.bucket(bucketName)
						.object(objectName)
						.build();
        return minioClient.getObject(args);
    }
	
	/**
	 * 獲取對象的臨時訪問url,有效期5分鍾
	 * */
    public String getObjectURL(String bucketName, String objectName) throws Exception{
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
								.bucket(bucketName)
								.object(objectName)
        							.expiry(5, TimeUnit.MINUTES)
							        .method(Method.GET)
        							.build();
        return minioClient.getPresignedObjectUrl(args);
    }
    
    /**
     * 刪除對象
     * */
    public void removeObject(String bucketName, String objectName) throws Exception {
    	RemoveObjectArgs args = RemoveObjectArgs.builder()
    						.bucket(bucketName)
    						.object(objectName)
    						.build();
    	minioClient.removeObject(args);
    	logger.info("bucket:{}文件{}已刪除", bucketName , objectName);
    }
    
    /**
     * 上傳MultipartFile
     * @param bucketName 文件存放的bucket
     * @param filePath 文件在bucket里的全目錄
     * */
    public ObjectWriteResponse uploadFile(String bucketName, String filePath, MultipartFile file) throws Exception{
    	return putObject(bucketName, filePath, file.getInputStream(), file.getSize(), file.getContentType());
    }
    
    /**
     * 從minio下載文件,直接通過response傳輸
     * */
    public void downloadFile(String bucketName, String filePath, HttpServletResponse response) throws Exception {
    	
		try (InputStream is = getObject(bucketName, filePath);
			 BufferedInputStream bis = new BufferedInputStream(is); 
			 OutputStream os = response.getOutputStream()) {
		//try {	
			/*InputStream is = getObject(bucketName, filePath);
			BufferedInputStream bis = new BufferedInputStream(is);
			OutputStream os = response.getOutputStream();*/
			response.setContentType("application/force-download;charset=utf-8");// 設置強制下載而不是直接打開
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filePath, "UTF-8"));// 設置文件名
            
            
            byte[] buffer = new byte[1024*1024*1024]; //buffer 1M
            int offset = bis.read(buffer);
            while (offset != -1) {
                os.write(buffer, 0, offset);
                offset = bis.read(buffer);
            }
            os.flush();
		} catch (Exception e) {
			logger.error("下載文件失敗"+e.getMessage(), e);
			throw new RuntimeException("下載文件失敗" , e);
		}
    }
}

記錄一下幾個問題:
1、springboot 2.1.13.RELEASE環境下集成了MinIO客戶端7.1.0版本,最新的版本貌似集成不進去
2、文件下載也就是minioClient.getObject(args)場景下,客戶端與MinIO server連接默認保持5分鍾,每隔15s由MinIO server向客戶端發送keep-alive check,5分鍾后由客戶端主動發起關閉連接。
3、文件下載時使用流的方式傳輸,上述代碼里使用try-with-resource的方式確保finally關閉流,但是通過wireshark觀測貌似直接普通try-catch最后也沒有造成連接的泄露,應該是底層的okio做了優化、5分鍾后直接發起了連接關閉。但作為好習慣還是應該主動去關閉流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM