马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
Java工程师实现minio存储大型视频文件网页实现分批加载视频播放
一、需求分析
老板给我出个标题,让我把的影戏文件上传到minio文件体系,再通过WEB端分配加载视频播放,雷同于我们平常的影戏网站。小编把Java代码共享出来。是真正的能拿过来直接用的。小编我切身测过。
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;
- import io.minio.GetObjectArgs;
- import io.minio.MinioClient;
- import io.minio.ObjectStat;
- import io.minio.StatObjectArgs;
- import io.minio.errors.*;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.MediaType;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestHeader;
- import org.springframework.web.bind.annotation.RestController;
- import com.sun.minio.util.MinIoUtil;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- @RestController
- public 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);
- return new ResponseEntity<>(videoBytes, headers, HttpStatus.PARTIAL_CONTENT);
- } else {
- headers.setContentLength(videoBytes.length);
- return new ResponseEntity<>(videoBytes, headers, HttpStatus.OK);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- return new 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">
- 您的浏览器不支持视频标签。
- </video>
- -->
-
- <video src="/minio_demo/testDisplay2/test" controls width="640" height="360">
- 您的浏览器不支持视频标签。
- </video>
-
-
- </div>
- </body>
- </html>
复制代码 1.5上传Controller
- package com.sun.controller;
- import io.minio.errors.*;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.core.io.support.PropertiesLoaderUtils;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.multipart.MultipartFile;
- import org.springframework.web.servlet.ModelAndView;
- import com.sun.minio.util.MinIoContentType;
- import com.sun.minio.util.MinIoUtil;
- import com.sun.minio.util.MinioBucketEnum;
- import com.sun.service.MinioServiceImpl;
- import javax.servlet.RequestDispatcher;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.InputStream;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.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" +
- " 您的浏览器不支持视频标签。\n" +
- "</video>";
- }
- // 构建包含两个a标签的HTML代码字符串
- StringBuilder htmlBuilder = new StringBuilder();
- htmlBuilder.append("<a href="/minio_demo/testClick/deleteFile?md5=").append(md5).append("">删除文件</a>");
- htmlBuilder.append(" "); // 增加一些空格间隔两个标签
- 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>";
- }
- return filePath;
- }
- @RequestMapping("/testMv1")
- public ModelAndView testMv1(HttpServletRequest request){
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.setViewName("/minio/demo");
- return modelAndView;
- }
- @RequestMapping("/testMv2")
- public ModelAndView testMv2(HttpServletRequest request){
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.setViewName("/minio/demo2");
- return modelAndView;
- }
- @RequestMapping("/testMv3")
- public ModelAndView testMv3(){
- ModelAndView mv = new ModelAndView();
- mv.setViewName("/minio/demo3");
- return mv;
- }
- }
复制代码 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: 0 0 10px 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;
- import io.minio.*;
- import io.minio.errors.*;
- import org.apache.commons.lang3.StringUtils;
- import org.joda.time.DateTime;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.core.io.support.PropertiesLoaderUtils;
- import org.springframework.web.multipart.MultipartFile;
- import com.sun.doman.MinioRecord;
- import com.sun.service.MinioServiceImpl;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletResponse;
- import javax.validation.constraints.NotNull;
- import java.io.*;
- import java.net.URLEncoder;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Properties;
- import java.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 {
- return minioClient.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);
- return map;
- }
- /**
- *
- * @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){
- return null;
- }
- 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);
- }
- return md5Val;
- }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){
- return null;
- }
- 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);
- }
- return md5Val;
- }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){
- return null;
- }
- return upload(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();
- return md5Str;
- }
- /**
- *
- * @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(".")){
- return preFix+md5Val;
- }
- String suffix = name.substring(name.lastIndexOf("."));
- return preFix+md5Val+suffix;
- }catch (Exception e){
- //最起码把md5的值返回作为文件名
- return md5Val;
- }
- }
- /**
- * 文件下载,可以下载到本地某个地址
- * @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();
- }
- return md5ValOrOldPath;
- }
- /**
- * 文件下载
- * @param os 目标输出流
- * @throws Exception
- */
- private static boolean loadOBbjectByMD5(MinioBucketEnum minioBucketEnum, OutputStream os, String md5Val)throws Exception{
- if(StringUtils.isBlank(md5Val)||os==null){
- return false;
- }
- MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);
- if(byMd5Val==null){
- return false;
- }
- 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();
- return true;
- }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())){
- return true;
- }
- return false;
- }catch (Exception e){
- return false;
- }
- }
- 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());
- return object;
- }catch (Exception e2){
- if(i==count-1){
- throw e2;
- }
- TimeUnit.MILLISECONDS.sleep(200);
- }
- }
- return null;
- }
- /**
- * 获取对象流
- * @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());
- }
- return object;
- }
- /**
- * 根据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){
- return false;
- }else{
- return true;
- }
- }
- /**
- * 返回minio数据库表的记录
- * @return
- */
- public static MinioRecord getMinioRecordByMd5Value(MinioBucketEnum minioBucketEnum, String minioMd5Value){
- return minioService.getByMd5Val(minioBucketEnum.getBucketName(),minioMd5Value);
- }
-
- }
复制代码- package com.sun.minio.util;
- import org.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)){
- return minIoContentType;
- }
- }
- return STREAM;
- }
- public static MinIoContentType getContentTypeByFileName(String fileName){
- if(StringUtils.isBlank(fileName)){
- return STREAM;
- }
- String substring = fileName.substring(fileName.lastIndexOf("."), fileName.length());
- if(StringUtils.isBlank(substring)){
- return STREAM;
- }
- substring = substring.toLowerCase();
- if("pdf".equals(substring)){
- return PDF;
- }
- if("png".equals(substring)||"jpg".equals(substring)||"jpng".equals(substring)||"gif".equals(substring)){
- return IMAGE;
- }
- 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)){
- return VEDIO;
- }
- if("txt".equals(substring)){
- return TEXT;
- }
- return STREAM;
- }
- }
复制代码- 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(){
- return bucketName;
- }
- }
复制代码- package com.sun.minio.util;
- import java.io.BufferedInputStream;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.InputStream;
- import java.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));
- return md5HashCode32(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");
- //分多次将一个文件读入,对于大型文件而言,比较推荐这种方式,占用内存比较少。
- 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。
- */
- hexValue.append("0");
- }
- //这里借助了Integer类的方法实现16进制的转换
- hexValue.append(Integer.toHexString(val));
- }
- return hexValue.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。
- */
- hexValue.append("0");
- }
- //这里借助了Integer类的方法实现16进制的转换
- hexValue.append(Integer.toHexString(val));
- }
- return hexValue.toString();
- } catch (Exception e) {
- e.printStackTrace();
- return "";
- }
- }
- }
复制代码- package com.sun.minio.util;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.util.StringUtils;
- import javax.servlet.http.HttpServletResponse;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.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);
- return false;
- } finally {
- if (closeInputStream) {
- inputStream.close();
- }
- out.flush();
- out.close();
- }
- return true;
- }
- 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");
- }
- }
复制代码
|