小编把Java代码共享出来

发布时间:2025-06-24 18:57:58  作者:北方职教升学中心  阅读量:880


需求说明

老板给我出个题目,让我把的电影文件上传到minio文件系统,再通过WEB端分配加载视频播放,类似于我们普通的电影网站。

1.1minio版本

<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.4</version></dependency>

1.2maven依赖

<?xml version="1.0"encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.sun</groupId><artifactId>springboot-sun</artifactId><version>1.0-SNAPSHOT</version><!--springboot工程需要继承的父工程--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.8.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--web开发的起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!--<scope>runtime</scope>--></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.12</version></dependency></dependencies></project>

1.3分段视频播放Controller

package com.sun.controller;importio.minio.GetObjectArgs;importio.minio.MinioClient;importio.minio.ObjectStat;importio.minio.StatObjectArgs;importio.minio.errors.*;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.HttpHeaders;importorg.springframework.http.HttpStatus;importorg.springframework.http.MediaType;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RequestHeader;importorg.springframework.web.bind.annotation.RestController;importcom.sun.minio.util.MinIoUtil;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;@RestControllerpublic class VideoController2 {@GetMapping("/testDisplay2/{fileName}")public ResponseEntity<byte[]>getVideo(@PathVariable String fileName,            @RequestHeader(value ="Range", required =false)String range){try {String bucketName ="movie";String objectName =fileName + ".mp4";MinioClient minioClient=MinIoUtil.minioClient;// 根据Range请求头来设置分批加载的范围            GetObjectArgs.Builder getObjectArgsBuilder =GetObjectArgs.builder().bucket(bucketName).object(objectName);long start=0l;if(range!=null &&range.startsWith("bytes=")){String[]values =range.split("=")[1].split("-");start =Long.parseLong(values[0]);long end =values.length >1? Long.parseLong(values[1]):-1;if(end!=-1){long length =end - start + 1;getObjectArgsBuilder.offset(start).length(length);}else{getObjectArgsBuilder.offset(start);}}// 获取视频流            try (InputStream inputStream =minioClient.getObject(getObjectArgsBuilder.build())){ByteArrayOutputStream outputStream =new ByteArrayOutputStream();byte[]buffer =new byte[1024* 1024];// 1MB缓冲区                int length;while((length =inputStream.read(buffer))!=-1){outputStream.write(buffer, 0, length);}byte[]videoBytes =outputStream.toByteArray();HttpHeaders headers =new HttpHeaders();headers.setContentType(MediaType.parseMediaType("video/mp4"));// 如果是分段请求,设置相应的响应头                if(range!=null){ObjectStat objectStat =minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());long fileSize =objectStat.length();headers.set("Content-Range", "bytes "+ start + "-"+ (start + videoBytes.length - 1)+ "/"+ fileSize);returnnew ResponseEntity<>(videoBytes, headers, HttpStatus.PARTIAL_CONTENT);}else{headers.setContentLength(videoBytes.length);returnnew ResponseEntity<>(videoBytes, headers, HttpStatus.OK);}}}catch (Exception e){e.printStackTrace();returnnew ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);}}}

1.4 html5 播放

<html><title>test</title><meta http-equiv="Content-Type"content="text/html; charset=utf-8"><head><style type="text/css">.upload {margin-top: 100px;margin-left: 100px;text-align: center;}</style></head><body><h1 style="text-align: center;margin-top: 20px">test</h1><div><!--  <img src="/minio_demo/testClick/testDisplay?url=/data/temp/img/a6efce1b9d16fdfabf36882ab08f8c5495ee7b9f.jpg">--></div><div><!--       <video src="/minio_demo/testClick/testDisplay?md5=a172e15a869fd7224618840c0815dcb1"controls width="640"height="360">您的浏览器不支持视频标签。                     */hexValue.append("0");}                //这里借助了Integer类的方法实现16进制的转换                hexValue.append(Integer.toHexString(val));}returnhexValue.toString();}catch (Exception e){e.printStackTrace();return"";}}}
package com.sun.minio.util;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.util.StringUtils;importjavax.servlet.http.HttpServletResponse;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.URLEncoder;public class DownloadUtils {private static final String JSON_APPLICATION ="application/json";private static final Logger log =LoggerFactory.getLogger(DownloadUtils.class.getName());public static boolean downloadFile(HttpServletResponse response, InputStream inputStream            , String objectName, long fileLen, String contentType            , boolean closeInputStream, long maxAge,String exportFileName)throws Exception {String fileName =StringUtils.isEmpty(exportFileName)?objectName.substring(objectName.lastIndexOf("/")+1):exportFileName;if(!StringUtils.isEmpty(contentType)){response.setContentType(contentType);}else{response.setContentType("application/octet-stream");}response.setCharacterEncoding("utf-8");response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));response.setHeader("X-Actual-Content-Length", String.valueOf(fileLen));cors(response, maxAge);OutputStream out =response.getOutputStream();byte[]buffer =new byte[1024];int len;try {while((len =inputStream.read(buffer))!=-1){out.write(buffer, 0, len);}}catch (Exception e){log.info("download file error.e="+ e);returnfalse;}finally {if(closeInputStream){inputStream.close();}out.flush();out.close();}returntrue;}public static void cors(HttpServletResponse response, long maxAge){response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");if(maxAge >0){response.setHeader("Access-Control-Max-Age", String.valueOf(maxAge));response.setHeader("Cache-Control", "max-age="+ maxAge);}response.setHeader("Access-Control-Allow-Headers", "*");response.setHeader("Access-Control-Expose-Headers", "Cache-Control, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, X-Actual-Content-Length, Content-Disposition");}}

在这里插入图片描述

byte[]buffer =new byte[1024];int length =-1;while((length =fis.read(buffer,0,1024))!=-1){md.update(buffer, 0, length);}fis.reset();//转换并返回包含16个元素字节数组,返回数值范围为-128到127 byte[]md5Bytes =md.digest();StringBuffer hexValue =new StringBuffer();for(int i =0;i <md5Bytes.length;i++){int val =((int)md5Bytes[i])&0xff;//解释参见最下方 if (val <16){ /***如果小于16,那么val值的16进制形式必然为一位, *因为十进制0,1...9,10,11,12,13,14,15对应的 16进制为 0,1...9,a,b,c,d,e,f;*此处高位补0</video></div></body></html>

1.5上传Controller

package com.sun.controller;importio.minio.errors.*;importorg.apache.commons.lang3.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.core.io.support.PropertiesLoaderUtils;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.ResponseBody;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.multipart.MultipartFile;importorg.springframework.web.servlet.ModelAndView;importcom.sun.minio.util.MinIoContentType;importcom.sun.minio.util.MinIoUtil;importcom.sun.minio.util.MinioBucketEnum;importcom.sun.service.MinioServiceImpl;importjavax.servlet.RequestDispatcher;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.InputStream;importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;importjava.util.Properties;@RestController@RequestMapping("testClick")public class MinioDemoController {@Autowired	private MinioServiceImpl minioServiceImpl;/**     * 直接页面展示     * @param response     */    @RequestMapping("/testDisplay")public void testDisplay(HttpServletResponse response,String md5)throws Exception {MinIoUtil.displayFile(minioServiceImpl,MinioBucketEnum.VIDEO_FILES,response,md5);}@RequestMapping("/testDownload")public void testDownLoad(HttpServletResponse response,String md5)throws Exception {MinIoUtil.downloadFile(minioServiceImpl,MinioBucketEnum.VIDEO_FILES,response,md5,"");}@RequestMapping("/testLoad")public void testLoadFile(HttpServletResponse response,String md5,String targetPath)throws  Exception{MinIoUtil.loadObject(MinioBucketEnum.VIDEO_FILES,targetPath,md5);}@RequestMapping("/deleteFile")public String deleteFile(String md5)throws  Exception{MinIoUtil.deleteObject(minioServiceImpl,MinioBucketEnum.VIDEO_FILES, md5);return"deleteFileSucceed";}@RequestMapping("/testUpload")@ResponseBody    public String testUpload(HttpServletRequest request, @RequestParam("multipartFile")MultipartFile[]multipartfiles,String url,String pucketName)throws  Exception{String filePath ="";for(MultipartFile multipartFile:multipartfiles){String contentType =multipartFile.getContentType();InputStream inputStream =multipartFile.getInputStream();if(!StringUtils.isBlank(url)&&!url.startsWith("/")){url ="/"+url;}if(!StringUtils.isBlank(url)&&!url.endsWith("/")){url +="/";}MinioBucketEnum minioBucketEnum =MinioBucketEnum.VIDEO_FILES;/*			 * if(pucketName.equals("monthlytext")){minioBucketEnum =* MinioBucketEnum.MONTHLY_TEXT;}if(pucketName.equals("email")){* minioBucketEnum =MinioBucketEnum.VIDEO_FILES;}* if(pucketName.equals("excel")){minioBucketEnum =MinioBucketEnum.EXCEL;}*/                        String md5 =MinIoUtil.upload(minioServiceImpl,minioBucketEnum,url + multipartFile.getOriginalFilename(), inputStream, MinIoContentType.getContentType(contentType));filePath+="<p>"+md5+"</p>";if(multipartFile.getOriginalFilename().contains(".mp4")){filePath ="<video src="/minio_demo/testClick/testDisplay?md5="+md5+""controls width="640"height="360">n"+                        "    您的浏览器不支持视频标签。                     */hexValue.append("0");}                //这里借助了Integer类的方法实现16进制的转换                hexValue.append(Integer.toHexString(val));}returnhexValue.toString();}catch (Exception e){e.printStackTrace();return"";}}/**     *字符串经过md5加密成32位     */    public static String md5HashCode32Str(String plainText){try {MessageDigest md =MessageDigest.getInstance("MD5");md.update(plainText.getBytes());byte[]md5Bytes  =md.digest();StringBuffer hexValue =new StringBuffer();for(int i =0;i <md5Bytes.length;i++){int val =((int)md5Bytes[i])&0xff;//解释参见最下方                if (val <16){                    /***如果小于16,那么val值的16进制形式必然为一位,                     *因为十进制0,1...9,10,11,12,13,14,15对应的 16进制为 0,1...9,a,b,c,d,e,f;*此处高位补0。小编我亲自测过。\n"+                        "</video>";}// 构建包含两个a标签的HTML代码字符串            StringBuilder htmlBuilder =new StringBuilder();htmlBuilder.append("<a href=\"/minio_demo/testClick/deleteFile?md5=").append(md5).append("\">删除文件</a>");htmlBuilder.append("&nbsp;&nbsp;&nbsp;&nbsp;");// 增加一些空格间隔两个标签            htmlBuilder.append("<a href=\"/minio_demo/testClick/testDownload?md5=").append(md5).append("\">下载文件</a>");// 得到最终的HTML字符串            String htmlString =htmlBuilder.toString();filePath=filePath+htmlString;// filePath+="<p>"+md5+"</p>";}returnfilePath;}@RequestMapping("/testMv1")public ModelAndView testMv1(HttpServletRequest request){ModelAndView modelAndView =new ModelAndView();modelAndView.setViewName("/minio/demo");returnmodelAndView;}@RequestMapping("/testMv2")public ModelAndView testMv2(HttpServletRequest request){ModelAndView modelAndView =new ModelAndView();modelAndView.setViewName("/minio/demo2");returnmodelAndView;}@RequestMapping("/testMv3")public ModelAndView testMv3(){ModelAndView mv=new ModelAndView();mv.setViewName("/minio/demo3");returnmv;}}

1.6 上传HTML5

<!DOCTYPE html><html lang="zh-CN"><head><title>文件上传页面</title><meta http-equiv="Content-Type"content="text/html; charset=utf-8"><style type="text/css">/* 整体页面样式 */        body {font-family: Arial, sans-serif;margin: 0;padding: 0;display: flex;flex-direction: column;justify-content: center;align-items: center;min-height: 100vh;background-color: #f4f4f4;}/* 标题样式 */        h1 {color: #333;margin-bottom: 30px;}/* 上传表单容器样式 */      .upload {background-color: #fff;padding: 30px;border-radius: 10px;box-shadow: 0010px rgba(0, 0, 0, 0.1);width: 400px;}/* 表单段落样式 */      .upload p {margin-bottom: 20px;}/* 文件选择输入框样式 */      .upload input[type="file"]{width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 5px;}/* 文本输入框样式 */      .upload input[type="text"]{width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 5px;}/* 提交按钮样式 */      .upload input[type="submit"]{background-color: #007BFF;color: #fff;padding: 10px 20px;border: none;border-radius: 5px;cursor: pointer;transition: background-color 0.3s ease;}.upload input[type="submit"]:hover {background-color: #0056b3;}</style></head><body><h1>文件上传至MinIO</h1><div class="upload"><form action="/minio_demo/testClick/testUpload"method="post"enctype="multipart/form-data"><p>选择文件: <input type="file"name="multipartFile"/></p><p>桶名称: <input type="text"name="pucketName"value="movie"/></p><p>文件目录: <input type="text"name="url"hidden /></p><p><input type="submit"value="上传并检测"/></p></form></div></body></html>

在这里插入图片描述

1.7 MinIoUtil工具类

package com.sun.minio.util;importio.minio.*;importio.minio.errors.*;importorg.apache.commons.lang3.StringUtils;importorg.joda.time.DateTime;importorg.slf4j.LoggerFactory;importorg.springframework.beans.BeansException;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.core.io.support.PropertiesLoaderUtils;importorg.springframework.web.multipart.MultipartFile;importcom.sun.doman.MinioRecord;importcom.sun.service.MinioServiceImpl;importjavax.servlet.ServletOutputStream;importjavax.servlet.http.HttpServletResponse;importjavax.validation.constraints.NotNull;importjava.io.*;importjava.net.URLEncoder;importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;importjava.util.HashMap;importjava.util.Map;importjava.util.Properties;importjava.util.concurrent.TimeUnit;/** * minio工具类 *//** * minio工具类 */public class MinIoUtil {private static MinioServiceImpl minioService =null;private static final org.slf4j.Logger Logger =LoggerFactory.getLogger(MinIoUtil.class);public static MinioClient minioClient =null;private static  String enpoint;private static  String username;private static  String password;static {try{Properties properties =PropertiesLoaderUtils.loadAllProperties("application.properties");enpoint =properties.getProperty("minioEnpoint");Logger.info("获取minio[enpoint]:"+enpoint);username =properties.getProperty("minioUserName");Logger.info("获取minio[minioUserName]:"+username);password =properties.getProperty("minioPassword");Logger.info("获取minio[minioPassword]"+password);minioClient =MinioClient.builder().endpoint(enpoint).credentials(username,password).build();for(MinioBucketEnum minioBucketEnum: MinioBucketEnum.values()){if(!bucketExist(minioBucketEnum.getBucketName())){createBucket(minioBucketEnum.getBucketName());}}}catch (Exception e){Logger.error("获取minio连接失败",e);e.printStackTrace();}}//检查桶是否存在    public static boolean bucketExist(String buckeyName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {returnminioClient.bucketExists(BucketExistsArgs.builder().bucket(buckeyName).build());}//创建桶    public static  void createBucket(String bucketName)throws IOException, InvalidKeyException, InvalidResponseException, RegionConflictException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}/**     *     * @return     */    public Map<String,String>getAccess(){Map<String,String>map =new HashMap<>();map.put("username",username);map.put("password",password);returnmap;}/**     *     * @param name 文件名,可以是单纯的文件名test.pdf,也可以是类似aaa/bbb/ccc/test.pdf,如果没有重复文件名,则会为在minio中的文件路径     * @param inputStream 文件流     * @param contentType     * @return     */    public static String upload(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, String name, InputStream inputStream, MinIoContentType contentType)throws Exception{if(StringUtils.isBlank(name)||inputStream==null){returnnull;}try{BufferedInputStream bis =new BufferedInputStream(inputStream);String md5Val =MD5Util.md5HashCode32(bis);MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){if(objectExist(minioBucketEnum.getBucketName(),name)){//minio存在同名文件,需要改一下文件名                    name =getOtherName(name,md5Val);}boolean flag =false;int count =3;for(int i =0;i<count;i++){//失败尝试3次.最后一次还失败抛异常                    if(flag){break;}try{minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(name).contentType(contentType ==null ? MinIoContentType.STREAM.contentType :contentType.contentType).stream(bis, bis.available(), partSize).build());flag =true;}catch (Exception e){if(i==count-1){throw e;}TimeUnit.MILLISECONDS.sleep(200);}}minioService.insertOne(MinioRecord.builder().bucketName(minioBucketEnum.getBucketName()).md5Val(md5Val).minioFilePath(name).remainNum(1).createTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).updateTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).build());}else{byMd5Val.setRemainNum((byMd5Val.getRemainNum()==null?0:byMd5Val.getRemainNum())+1);minioService.updateRemainNum(byMd5Val);}returnmd5Val;}catch (Exception e){Logger.error("上传文件失败,name:"+name,e);throw e;}finally {try{inputStream.close();}catch (Exception e){}}}public static String upload(MinioBucketEnum minioBucketEnum, String name, InputStream inputStream, MinIoContentType contentType)throws Exception{if(StringUtils.isBlank(name)||inputStream==null){returnnull;}try{BufferedInputStream bis =new BufferedInputStream(inputStream);String md5Val =MD5Util.md5HashCode32(bis);MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){if(objectExist(minioBucketEnum.getBucketName(),name)){//minio存在同名文件,需要改一下文件名                    name =getOtherName(name,md5Val);}boolean flag =false;int count =3;for(int i =0;i<count;i++){//失败尝试3次.最后一次还失败抛异常                    if(flag){break;}try{minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(name).contentType(contentType ==null ? MinIoContentType.STREAM.contentType :contentType.contentType).stream(bis, bis.available(), partSize).build());flag =true;}catch (Exception e){if(i==count-1){throw e;}TimeUnit.MILLISECONDS.sleep(200);}}minioService.insertOne(MinioRecord.builder().bucketName(minioBucketEnum.getBucketName()).md5Val(md5Val).minioFilePath(name).remainNum(1).createTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).updateTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).build());}else{byMd5Val.setRemainNum((byMd5Val.getRemainNum()==null?0:byMd5Val.getRemainNum())+1);minioService.updateRemainNum(byMd5Val);}returnmd5Val;}catch (Exception e){Logger.error("上传文件失败,name:"+name,e);throw e;}finally {try{inputStream.close();}catch (Exception e){}}}/**     *     * @param name 文件名,可以是单纯的文件名test.pdf,也可以是类似aaa/bbb/ccc/test.pdf,如果没有重复文件名,则会为在minio中的文件路径     * @param filePath 需要上传的文件路径     * @param contentType     * @return     */    public static String upload(MinioBucketEnum minioBucketEnum, String name, String filePath, MinIoContentType contentType)throws Exception {File file=new File(filePath);if(!file.exists()||minioBucketEnum==null){returnnull;}returnupload(minioBucketEnum,name, new FileInputStream(file), contentType);}/**     * 基于MultipartFile文件上传     * @param minioBucketEnum 桶信息     * @param file文件     * @param contentType 内容类型     * @return 返回结果     * @throws Exception 异常     */    public static String upload(@NotNull MinioBucketEnum minioBucketEnum, @NotNull MultipartFile file, MinIoContentType contentType)throws Exception {String filename =file.getOriginalFilename();byte[]bytes =file.getBytes();ByteArrayInputStream bais =new ByteArrayInputStream(bytes);String md5Str =upload(minioBucketEnum, filename, bais, contentType);bais.close();returnmd5Str;}/**     *     * @param response     * @param md5ValOrOldPath 新文件上传则是md5值,旧文件则依然使用旧文件的路径     * @param exportFileName 最终给到前端的文件名     */    public static void downloadFile(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5ValOrOldPath,String exportFileName){downloadFileByMd5(minioService,minioBucketEnum,response,checkOutMd5Val(md5ValOrOldPath),exportFileName);}/**     * 根据文件md5值获取     * @param md5Val     * @return     */    private static void downloadFileByMd5(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5Val,String exportFileName){if(StringUtils.isBlank(md5Val)||response==null){return;}try {MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val!=null){ObjectStat objectStat =minioClient.statObject(StatObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build());DownloadUtils.downloadFile(response, minioClient.getObject(GetObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build()), objectStat.name(), objectStat.length(), objectStat.contentType(), true, -1,exportFileName);}}catch (Exception e){Logger.error("下载文件失败",e);e.printStackTrace();}}/**     *     * @param response     * @param md5ValOrOldPath 新文件上传则是md5值,旧文件则依然使用旧文件的路径,最终都是通过文件md5找文件     * @throws Exception     */    public static void displayFile(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5ValOrOldPath)throws Exception {displayFileByMd5(minioService ,minioBucketEnum,response,checkOutMd5Val(md5ValOrOldPath));}/**     * 根据MD5值展示文件     * @param response     * @param md5Val     */    private static void displayFileByMd5(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5Val)throws Exception {MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);ObjectStat objectStat =minioClient.statObject(StatObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build());String objectName =objectStat.name();String fileName =objectName.substring(objectName.lastIndexOf("/")+1);response.setCharacterEncoding("utf-8");response.setHeader("Content-Type",objectStat.contentType());response.addHeader("Content-Disposition","inline;filename="+  URLEncoder.encode(fileName, "UTF-8"));response.addHeader("Content-Length",""+ objectStat.length());try(ServletOutputStream outputStream =response.getOutputStream();BufferedInputStream bufferedInputStream =new BufferedInputStream(getObjectInputStream(minioBucketEnum.getBucketName(),objectName));BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(outputStream);){int lenth;byte[]bytes =new byte[1024];while((lenth=bufferedInputStream.read(bytes,0,bytes.length))!=-1){bufferedOutputStream.write(bytes,0,lenth);}bufferedOutputStream.flush();}catch (Exception e){Logger.error("下载失败objectname:"+objectName,e);}}/**     *     * @param name     * @param md5Val     * @return     */    private static String getOtherName(String name,String md5Val){try{String preFix =name.substring(0,name.lastIndexOf("/")+1);if(!name.contains(".")){returnpreFix+md5Val;}String suffix =name.substring(name.lastIndexOf("."));returnpreFix+md5Val+suffix;}catch (Exception e){//最起码把md5的值返回作为文件名            returnmd5Val;}}/**     * 文件下载,可以下载到本地某个地址     * @param targetFilaPath 目标文件     * @param md5ValOrOldPath 要下载到的目标文件地址,文件如果已经存在就无法下载     * @throws Exception     */    public static void loadObject(MinioBucketEnum minioBucketEnum, String targetFilaPath, String md5ValOrOldPath)throws Exception{loadOBbjectByMD5(minioBucketEnum,targetFilaPath,checkOutMd5Val(md5ValOrOldPath));}/**     * 文件下载,可以下载到本地某个地址     * @param targetFilePath 要下载到的目标文件地址,文件如果已经存在就无法下载     * @throws Exception     */    private static void loadOBbjectByMD5(MinioBucketEnum minioBucketEnum, String targetFilePath, String md5Val)throws Exception {if(StringUtils.isBlank(targetFilePath)){return;}MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){return;}File file=new File(targetFilePath);if(file.exists()){return;}file.createNewFile();loadOBbjectByMD5(minioBucketEnum,new FileOutputStream(file),md5Val);}/**     *     * @param os     * @param md5ValOrOldPath 新文件上传则是md5值,旧文件则依然使用旧文件的路径,最终都是通过文件md5找文件     * @throws Exception     */    public static void loadObject(MinioBucketEnum minioBucketEnum, OutputStream os, String md5ValOrOldPath)throws Exception{//先判断是不是32位的md5值        loadOBbjectByMD5(minioBucketEnum,os,checkOutMd5Val(md5ValOrOldPath));}/**     * 看下传入参数是否是md5值,为md5值则直接返回..如果是旧文件系统保留的文件路径,需要看下迁移的文件中是否有过该文件,有则返回该文件的实际md5值     * 虽然可能会查两次数据库,不过问题不大,这表数据并不大     * @param md5ValOrOldPath     * @return     */    private static String checkOutMd5Val(String md5ValOrOldPath){//先判断是不是32位的md5值        if(StringUtils.isBlank(md5ValOrOldPath)){return"";}if(md5ValOrOldPath.length()!=32||md5ValOrOldPath.contains(".")||md5ValOrOldPath.contains("/")||md5ValOrOldPath.contains("\\")){String code =MD5Util.md5HashCode32Str(md5ValOrOldPath);MinioRecord byOldPath =minioService.getByOldPathMD5(code);if(byOldPath==null){return"";}md5ValOrOldPath =byOldPath.getMd5Val();}returnmd5ValOrOldPath;}/**     * 文件下载     * @param os 目标输出流     * @throws Exception     */    private static boolean loadOBbjectByMD5(MinioBucketEnum minioBucketEnum, OutputStream os, String md5Val)throws Exception{if(StringUtils.isBlank(md5Val)||os==null){returnfalse;}MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){returnfalse;}try(BufferedInputStream bufferedInputStream =new BufferedInputStream(getObjectInputStream(minioBucketEnum.getBucketName(),byMd5Val.getMinioFilePath()));BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(os);){int lenth;byte[]bytes =new byte[1024];while((lenth=bufferedInputStream.read(bytes,0,bytes.length))!=-1){bufferedOutputStream.write(bytes,0,lenth);}bufferedOutputStream.flush();returntrue;}catch (Exception e){Logger.error("下载失败objname:"+byMd5Val.getMinioFilePath(),e);throw e;}}/**     * true存在     * @param bucketName     * @param name     * @return     */    private static boolean objectExist(String bucketName,String name)throws Exception{try{ObjectStat objectStat =minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(name).build());if(!StringUtils.isBlank(objectStat.name())){returntrue;}returnfalse;}catch (Exception e){returnfalse;}}private static final long partSize =1024*1024*5l;/**     * 覆盖并上传文件     * @param bucketName     * @param name     * @param inputStream     * @throws Exception     */    /**     * 获取对象流     * @param bucketName     * @param objectName     * @return     */    private static InputStream getObjectInputStream(String bucketName,String objectName)throws Exception{int count =3;for(int i =0;i<count;i++){try{InputStream object =minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());returnobject;}catch (Exception e2){if(i==count-1){throw e2;}TimeUnit.MILLISECONDS.sleep(200);}}returnnull;}/**     * 获取对象流     * @param bucketName     * @param objectName     * @return     */    public static InputStream getObjectInputStreamByMd5Val(String bucketName,String objectName)throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {MinioRecord byMd5Val =minioService.getByMd5Val(bucketName, objectName);InputStream object =null;try {object =minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(byMd5Val.getMinioFilePath()).build());}catch (Exception e){e.printStackTrace();object =minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(byMd5Val.getMinioFilePath()).build());}returnobject;}/**     * 根据md5值删除     * 新文件上传则是md5值,旧文件则依然使用旧文件的路径,最终都是通过文件md5找文件     * @return     */    public static void deleteObject(MinioServiceImpl minioService,MinioBucketEnum minioBucketEnum, String md5ValOrOldPath)throws Exception{String md5Val =checkOutMd5Val(md5ValOrOldPath);MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){return;}if(byMd5Val.getRemainNum()!=null&&byMd5Val.getRemainNum()<=1){if(objectExist(minioBucketEnum.getBucketName(),byMd5Val.getMinioFilePath())){minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build());minioService.deleteRecord(minioBucketEnum.getBucketName(),md5Val);}}else{Integer remainNum =byMd5Val.getRemainNum();if(remainNum==null){byMd5Val.setRemainNum(1);}else{byMd5Val.setRemainNum(remainNum-1);}minioService.updateRemainNum(byMd5Val);}}/**     * 仅供判断该文件是否存在.     * @param minioMd5Value     * @return     */    public static boolean isExist(MinioBucketEnum minioBucketEnum, String minioMd5Value){MinioRecord byMd5Val =minioService.getByMd5Val(minioBucketEnum.getBucketName(),minioMd5Value);if(byMd5Val==null){returnfalse;}else{returntrue;}}/**     * 返回minio数据库表的记录     * @return     */    public static MinioRecord getMinioRecordByMd5Value(MinioBucketEnum minioBucketEnum, String minioMd5Value){returnminioService.getByMd5Val(minioBucketEnum.getBucketName(),minioMd5Value);}}
package com.sun.minio.util;importorg.apache.commons.lang3.StringUtils;public enum MinIoContentType {PDF("application/pdf"),    STREAM("application/octet-stream"),    IMAGE("image/jpeg"),    VEDIO("video/mp4"),    TEXT("text/html");public String contentType;private MinIoContentType(String contentType){this.contentType =contentType;}public static MinIoContentType getContentType(String contentType){for(MinIoContentType minIoContentType :MinIoContentType.values()){if(minIoContentType.contentType.equals(contentType)){returnminIoContentType;}}returnSTREAM;}public static MinIoContentType getContentTypeByFileName(String fileName){if(StringUtils.isBlank(fileName)){returnSTREAM;}String substring =fileName.substring(fileName.lastIndexOf("."), fileName.length());if(StringUtils.isBlank(substring)){returnSTREAM;}substring =substring.toLowerCase();if("pdf".equals(substring)){returnPDF;}if("png".equals(substring)||"jpg".equals(substring)||"jpng".equals(substring)||"gif".equals(substring)){returnIMAGE;}if("mp4".equals(substring)||"avi".equals(substring)||"mkv".equals(substring)||"mov".equals(substring)||"rmvb".equals(substring)||"FLV".equals(substring)||"rmvb".equals(substring)||"rm".equals(substring)){returnVEDIO;}if("txt".equals(substring)){returnTEXT;}returnSTREAM;}}
package com.sun.minio.util;public enum MinioBucketEnum {//EMAIL("email"),    //EXCEL("excel"),    //TYPICAL("typical"),    //FIRE_CONTROL("firecontrol"),    //MONTHLY_TEXT("monthlytext");VIDEO_FILES("movie");private String bucketName;private MinioBucketEnum(String bucketName){this.bucketName =bucketName;}public String getBucketName(){returnbucketName;}}
package com.sun.minio.util;importjava.io.BufferedInputStream;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.InputStream;importjava.security.MessageDigest;/** * 网上copy的代码,获取文件的32位MD5值 */public class MD5Util {/**     * 保证文件的MD5值为32位     * @param filePath	文件路径     * @return     * @throws FileNotFoundException     */    public static String md5HashCode32(String filePath)throws Exception{BufferedInputStream bfis =new BufferedInputStream(new FileInputStream(filePath));returnmd5HashCode32(bfis);}/**     * java计算文件32位md5值     * @param fis 输入流     * @return     */    public static String md5HashCode32(InputStream fis){try {fis.mark(Integer.MAX_VALUE);//拿到一个MD5转换器,如果想使用SHA-1或SHA-256,则传入SHA-1,SHA-256            MessageDigest md =MessageDigest.getInstance("MD5");//分多次将一个文件读入,对于大型文件而言,比较推荐这种方式,占用内存比较少。</video>--><video src="/minio_demo/testDisplay2/test"controls width="640"height="360">您的浏览器不支持视频标签。

Java工程师实现minio存储大型视频文件网页实现分批加载视频播放

在这里插入图片描述

一、是真正的能拿过来直接用的。小编把Java代码共享出来。