javascript
SpringBoot整合Minio实现文件上传、下载
SpringBoot整合Minio實現文件上傳、下載:
1,介紹高性能分布式存儲文件服務Minio:Minio是基于Go語言編寫的對象存儲服務,適合于存儲大容量非結構化的數據,例如圖片、音頻、視頻、日志文件、備份數據和容器/虛擬機鏡像等,而一個對象文件可以是任意大小,從幾kb到最大5T不等。區別于分布式存儲系統,minio的特色在于簡單、輕量級,對開發者友好!!!,認為存儲應該是一個開發問題而不是一個運維問題。
Minio的高可用設計特性:
- 分布式設計
1加粗樣式、Minio將多個硬盤(可以分布在不同機器)組成一個分布式的對象存儲系統
2、去中心化、分布式
- 數據保護
1、Minio采用糾刪碼(erasuer code)來保證集群多點或者單點故障和位衰減(bit rot)
2、最少需要4個節點來使Minio自動引入糾刪碼
- 高可用
分布式Minio存儲系統允許集群N/2節點宕機,但是恢復數據則需要通過N/2+1節點來進行恢復。
例如:8個節點,宕機4個,則需要5個節點進行數據恢復
- 限制
分布式Minio單個租戶最小需要4個節點(硬盤),最大支持16個節點(硬盤)受限于糾刪碼(erasuer
code),我們可以使用k8s來進行minio租戶的管理。
- 一致性
Minio不論是分布式或者是單機,均遵從read-after-write來保證數據的一致性
- 安全
支持TLS證書
- Minio糾刪碼
Minio使用糾刪碼(erasuer code) 進行校驗來保護數據,
避免發生硬件故障或者某種原因導致數據丟失,即使丟失N/2 的硬盤都可以將數據恢復回來。
http://www.360doc.com/content/21/1004/21/25921839_998266807.shtml 通過這個鏈接了解糾刪碼
Minio如何使用糾刪碼及優點
糾刪碼是一種數據丟失和損壞數據的一種算法,Minio使用Reed-Solomon
code拆分對象為N/2數據和N/2奇偶校驗塊。
例如:我有12塊硬盤,存放了一個文件,Minio會給我分成6份數據和6個奇偶校驗塊,不管我是丟失了哪個硬盤,數據均可以得到恢復。
.
Minio可以針對某個對象進行單獨恢復,而raid則需要針對卷來進行恢復,從而減少了恢復的時間周期。
Minio對每個對象進行編碼,存儲服務一經部署,存儲硬盤一般不需要進行更換或者修復,Minio的糾刪碼設計目標是為了性能和硬件加速。
接下來單機版Docker部署Minio服務:
通過配置docker-compose.yml文件管理容器,通過docker-compose up -d 啟動容器
docker-compose.yml文件
這里的9100是配置訪問minio自帶的web管理后臺,9090是配置的服務器
啟動容器,訪問web界面,如圖所示
.
.
minio服務搭建完畢之后,接下來就是用Springboot整合Minio實現文件的上傳、下載
項目練習源碼gitee地址: https://gitee.com/xzq25_com/minio-test.git
項目目錄結構:
拉取以上項目源碼,簡單總結一下需要干的事兒:
1,yml文件配置
2,minio配置類并注入容器進行管理
3,封裝minio操作服務器的API的工具類,項目中封裝了單文件上傳、下載、刪除、查看功能,桶的創建等
公司具體的存儲業務,在次基礎上根據公司業務來自行擴展
4,編寫controller層,用postman進行簡單的測試(測試類型:文本、文檔、表格、音頻、視頻、圖片)
yml配置
spring:# 配置文件上傳大小限制servlet:multipart:max-file-size: 200MBmax-request-size: 200MB minio:server: http://120.76.159.196port: 9090accessKey: rootsecretKey: 12345678bucket: testurlprefix: /minio/配置類截圖如下:
/*** @Author xiaozq* @Date 2022/11/17 9:54* <p>@Description: 注意:這里不能用@Data,需手動寫gitter,setter* reason:與@ConfigurationProperties一起用的時候,配置文件的值不能賦值給對應類屬性,屬性值均為null</p>*/ @Configuration @ConfigurationProperties(prefix = "minio") public class MinioConfig {private String server;private int port;private String accessKey;private String secretKey;// 桶名private String bucket;// 統一前綴private String urlPrefix;/*** 創建minio連接對象* @return*/@Beanpublic MinioClient minioClient(){return MinioClient.builder().endpoint(server,port,false).credentials(accessKey,secretKey).build();}public void setServer(String server) {this.server = server;}public void setPort(int port) {this.port = port;}public void setAccessKey(String accessKey) {this.accessKey = accessKey;}public void setSecretKey(String secretKey) {this.secretKey = secretKey;}public void setBucket(String bucket) {this.bucket = bucket;}public void setUrlPrefix(String urlPrefix) { this.urlPrefix = urlPrefix; }public String getUrlPrefix() {return urlPrefix;}public String getServer() {return server;}public int getPort() {return port;}public String getAccessKey() {return accessKey;}public String getSecretKey() {return secretKey;}public String getBucket() {return bucket;} }自定義封裝操作minio的api工具類
*** @Author xiaozq* @Date 2022/8/5 10:42* <p>@Description:</p>*/ @Component public class MinioTemplate {@Autowiredprivate MinioClient minioClient;@Value("${minio.bucket}")public String bucketName;@Value("${minio.urlprefix}")public String urlprefix;/*** 判斷bucket是否存在,不存在則創建* @param name*/public void existBucket(String name){try {boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if(!exist){minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());}}catch (Exception e){e.printStackTrace();}}/*** 創建存儲bucket* @param bucketName 存儲bucket名稱* @return Boolean*/public Boolean makeBucket(String bucketName) {try {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 刪除存儲bucket* @param bucketName 存儲bucket名稱* @return Boolean*/public Boolean removeBucket(String bucketName) {try {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 文件上傳* @param file* @return*/public Map<String, String> upload(MultipartFile file) {String filename = FileUtils.extractUploadFilename(file);try {InputStream inputStream = file.getInputStream();// 上傳到minio服務器minioClient.putObject(PutObjectArgs.builder().bucket(this.bucketName).object(filename).stream(inputStream, -1L, 10485760L).build());} catch (Exception e) {e.printStackTrace();}// 返回地址Map<String,String > resultMap = new HashMap<>();resultMap.put("url",filename);return resultMap;}/*** 文件下載* @param fileName 文件名* @param delete 是否刪除* @throws IOException*/public void fileDownload(@RequestParam(name = "fileName") String fileName,@RequestParam(defaultValue = "false") Boolean delete,HttpServletResponse response) {InputStream inputStream = null;OutputStream outputStream = null;try {if (StringUtils.isBlank(fileName)) {response.setHeader("Content-type", "text/html;charset=UTF-8");String data = "文件下載失敗";OutputStream ps = response.getOutputStream();ps.write(data.getBytes("UTF-8"));return;}outputStream = response.getOutputStream();// 獲取文件對象inputStream =minioClient.getObject(GetObjectArgs.builder().bucket(this.bucketName).object(fileName).build());byte buf[] = new byte[1024];int length = 0;response.reset();response.setHeader("Content-Disposition", "attachment;filename=" +URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8"));response.setContentType("application/octet-stream");response.setCharacterEncoding("UTF-8");// 輸出文件while ((length = inputStream.read(buf)) > 0) {outputStream.write(buf, 0, length);}inputStream.close();// 判斷:下載后是否同時刪除minio上的存儲文件if (BooleanUtils.isTrue(delete)) {minioClient.removeObject(RemoveObjectArgs.builder().bucket(this.bucketName).object(fileName).build());}} catch (Throwable ex) {response.setHeader("Content-type", "text/html;charset=UTF-8");String data = "文件下載失敗";try {OutputStream ps = response.getOutputStream();ps.write(data.getBytes("UTF-8"));}catch (IOException e){e.printStackTrace();}} finally {try {outputStream.close();if (inputStream != null) {inputStream.close();}}catch (IOException e){e.printStackTrace();}}}/*** 查看文件對象* @param bucketName 存儲bucket名稱* @return 存儲bucket內文件對象信息*/public List<ObjectItem> listObjects(String bucketName) {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());List<ObjectItem> objectItems = new ArrayList<>();try {for (Result<Item> result : results) {Item item = result.get();ObjectItem objectItem = new ObjectItem();objectItem.setObjectName(item.objectName());objectItem.setSize(item.size());objectItems.add(objectItem);}} catch (Exception e) {e.printStackTrace();return null;}return objectItems;}/*** 批量刪除文件對象* @param bucketName 存儲bucket名稱* @param objects 對象名稱集合*/public Map<String, String> removeObjects(String bucketName, List<String> objects) {Map<String,String > resultMap = new HashMap<>();List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());try {minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());resultMap.put("mes","刪除成功");}catch (Exception e){e.printStackTrace();resultMap.put("mes","網絡異常,刪除失敗");}return resultMap;}}上傳文件名替換工具類:
public class FileUtils {/*** 編碼文件名* 日期路徑 + UUID* 示例:fileName=2022/11/18/統計報表1668758006562.txt*/public static final String extractUploadFilename(MultipartFile file){String fileName = file.getOriginalFilename();// 注意,這里需要加上 \\ 將 特殊字符 . 轉意 \\. ,否則異常String[] fileArray = fileName.split("\\.");fileName = datePath() + "/" + fileArray[0]+System.currentTimeMillis()+"."+fileArray[1];return fileName;}/*** 日期路徑 即年/月/日 如2018/08/08*/public static final String datePath() {Date now = new Date();return DateFormatUtils.format(now, "yyyy/MM/dd");} }controller層測試
@RestController public class MinioController {@Autowiredprivate MinioTemplate minioTemplate;@Value("${minio.server}")private String server;@Value("${minio.port}")private Integer port;@Value("${minio.bucket}")private String bucket;/*** 單文件上傳* @param file 文件* @return*/@PostMapping("/uploadone")public Object uploadOne(MultipartFile file) {return minioTemplate.upload(file);}/*** 單文件下載* @param fileName 文件* @param delete 下載完后是否刪除, 請謹慎傳參*/@GetMapping("/download")public void download(@RequestParam(value = "fileName") String fileName,@RequestParam(defaultValue = "false")Boolean delete, HttpServletResponse response) {minioTemplate.fileDownload(fileName,delete,response);}/*** 查看存儲的文件列表* @param bucket 桶* @return*/@GetMapping("/list")public Object fileList(@RequestParam(value = "bucket") String bucket) {return minioTemplate.listObjects(bucket);}/*** 刪除文件* @param bucket 桶* @param list 文件名列表* @return*/@DeleteMapping ("/remove")public Object fileremove(@RequestParam String bucket,@RequestParam List<String> list) {return minioTemplate.removeObjects(bucket,list);}}.
接下來就是用postman測試,非常便利:測試存儲文檔、表格、日志、視頻、音頻、圖片等上傳、下載
.
好啦,Minio的入門學習就到此結束啦,希望能給你帶來幫助…
總結
以上是生活随笔為你收集整理的SpringBoot整合Minio实现文件上传、下载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: U8-固定资产月末结账报错:BOF或EO
- 下一篇: Kaminari分页