当前位置: 首页 > article >正文

Java工程师实现视频文件上传minio文件系统存储及网页实现分批加载视频播放

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("&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>";
        }
        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");
    }
}

在这里插入图片描述


http://www.kler.cn/a/463164.html

相关文章:

  • Niushop商城商业插件_cps联盟_包装转换_视频购物_同城配送_上门预约等插件的安装方法
  • DepthLab: From Partial to Complete 论文解读
  • 在 SQL 中,区分 聚合列 和 非聚合列(nonaggregated column)
  • 卸载干净 IDEA(图文讲解)
  • Java项目实战II基于微信小程序的家庭大厨(开发文档+数据库+源码)
  • Unity3D ILRuntime开发原则与接口绑定详解
  • 12.30-1-5学习周报
  • 【时时三省】(C语言基础)动态内存函数realloc
  • Node.js 常用命令全攻略
  • VSCode 插件开发实战(十三):如何添加个性化欢迎信息
  • Whiteboard-of-Thought——让大语言模型在白板上写下它们的推理过程,可以大大提高模型在视觉推理能力
  • Github - 如何提交一个带有“verified”标识的commit
  • 【漫话机器学习系列】031.数据增强(Dateset augmentation)
  • 安装、快速入门
  • npm 切换镜像源
  • MySQL5.7主从同步配置
  • mysql查询报错java.sql.SQLException: Illegal mix of collations for operation ‘UNION‘
  • 【智行安全】基于Synaptics SL1680的AI疲劳驾驶检测方案
  • 原生js封装ajax请求以及css实现提示效果和禁止点击效果
  • Android笔试面试题AI答之Android基础(9)
  • 扩充vmware磁盘大小以及分区
  • 细讲前端工程化
  • 使用SDL2搭建简易LVGL模拟器
  • 香港 GPU 服务器托管引领 AI 创新,助力 AI 发展
  • Ubuntu 上高效实现 Texlive 安装和管理
  • 关于flinkCDC监控mysql binlog时,datetime类型自动转换成时间戳类型问题