Minio8.*java工具类

package com.common.minio;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioConfig {

    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    @Bean
    public MinioUtils creatMinioClient() {
        return new MinioUtils(endpoint, accessKey, secretKey);
    }
}


package com.common.minio;

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: haoruijie
 * @Description: Minio工具类
 */
public class MinioUtils {

    private static MinioClient minioClient;
    private static String endpoint;
    private static String accessKey;
    private static String secretKey;

    private static final String SEPARATOR = "/";

    private MinioUtils() {
    }

    public MinioUtils(String endpoint, String accessKey, String secretKey) {
        MinioUtils.endpoint = endpoint;
        MinioUtils.accessKey = accessKey;
        MinioUtils.secretKey = secretKey;
        createMinioClient();
    }

    /**
     * 创建minioClient
     */
    public void createMinioClient() {
        try {
            if (null == minioClient) {
                minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
                System.err.println("创建Minio通道成功!");
            }
        } catch (Exception e) {
            System.err.println("创建Minio通道失败!");
        }
    }

    /**
     * 获取上传文件的基础路径
     *
     * @return url
     */
    public static String getBasisUrl(String bucketName) {
        return endpoint + SEPARATOR + bucketName + SEPARATOR;
    }

    /**
     * 验证bucketName是否存在
     *
     * @return boolean true:存在
     */
    public static boolean bucketExists(String bucketName)
            throws Exception {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建bucket
     *
     * @param bucketName bucket名称
     */
    public static void createBucket(String bucketName)
            throws Exception {
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 获取全部bucket
     */
    public static List getAllBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 根据bucketName获取信息
     *
     * @param bucketName bucket名称
     */
    public static Optional getBucket(String bucketName) throws Exception {
        return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public static void removeBucket(String bucketName) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 判断文件是否存在
     *
     * @param bucketName 存储桶
     * @param objectName 对象
     * @return true:存在
     */
    public static boolean doesObjectExist(String bucketName, String objectName) {
        boolean exist = true;
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param bucketName 存储桶
     * @param objectName 文件夹名称(去掉/)
     * @return true:存在
     */
    public static boolean doesFolderExist(String bucketName, String objectName) {
        boolean exist = false;
        try {
            Iterable> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());
            for (Result result : results) {
                Item item = result.get();
                if (item.isDir() && objectName.equals(item.objectName())) {
                    exist = true;
                }
            }
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return MinioItem 列表
     */
    public static List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) throws Exception {
        List list = new ArrayList<>();
        Iterable> objectsIterator = minioClient.listObjects(
                ListObjectsArgs.
                        builder().
                        bucket(bucketName).
                        prefix(prefix).
                        recursive(recursive).
                        build());
        if (objectsIterator != null) {
            for (Result o : objectsIterator) {
                Item item = o.get();
                list.add(item);
            }
        }
        return list;
    }

    /**
     * 通过MultipartFile,上传文件
     *
     * @param bucketName  存储桶
     * @param file        文件
     * @param objectName  对象名
     * @param contentType 文件类型
     */
    public static ObjectWriteResponse putObject(String bucketName, MultipartFile file, String objectName, String contentType) throws Exception {
        InputStream inputStream = file.getInputStream();
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType)
                        .stream(inputStream, inputStream.available(), -1).build());
    }

    /**
     * 通过MultipartFile,上传文件
     *
     * @param bucketName 存储桶
     * @param file       文件
     * @param objectName 对象名
     */
    public static ObjectWriteResponse putObject(String bucketName, MultipartFile file, String objectName) throws Exception {
        InputStream inputStream = file.getInputStream();
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName)
                        .stream(inputStream, inputStream.available(), -1).build());
    }

    /**
     * 上传本地文件
     *
     * @param bucketName 存储桶
     * @param objectName 对象名称
     * @param fileName   本地文件路径
     */
    public static ObjectWriteResponse putObject(String bucketName, String objectName, String fileName)
            throws Exception {
        return minioClient.uploadObject(
                UploadObjectArgs.builder()
                        .bucket(bucketName).object(objectName).filename(fileName).build());
    }

    /**
     * 通过流上传文件
     *
     * @param bucketName  存储桶
     * @param objectName  文件对象
     * @param inputStream 文件流
     */
    public static ObjectWriteResponse putObject(String bucketName, String objectName,
                                                InputStream inputStream)
            throws Exception {
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                        inputStream, inputStream.available(), -1).build());
    }

    /**
     * 创建文件夹或目录
     *
     * @param bucketName 存储桶
     * @param objectName 目录路径
     */
    public static ObjectWriteResponse putDirObject(String bucketName, String objectName)
            throws Exception {
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                        new ByteArrayInputStream(new byte[]{}), 0, -1).build());
    }

    /**
     * 拷贝文件
     *
     * @param bucketName    bucket名称
     * @param objectName    文件名称
     * @param srcBucketName 目标bucket名称
     * @param srcObjectName 目标文件名称
     */
    public static ObjectWriteResponse copyObject(String bucketName, String objectName,
                                                 String srcBucketName, String srcObjectName)
            throws Exception {
        return minioClient.copyObject(
                CopyObjectArgs.builder()
                        .source(CopySource.builder().bucket(bucketName).object(objectName).build())
                        .bucket(srcBucketName)
                        .object(srcObjectName)
                        .build());
    }

    /**
     * 文件下载
     *
     * @param bucketName 桶名称
     * @param request    请求
     * @param response   请求响应
     */
    public static void downloadFile(String bucketName, String originalName,
                                    HttpServletRequest request,
                                    HttpServletResponse response) {
        try {
            InputStream file = getObject(bucketName, originalName);
            //文件名乱码处理
            String useragent = request.getHeader("USER-AGENT").toLowerCase();
            if (useragent.contains("msie") || useragent.contains("like gecko") || useragent.contains("trident")) {
                originalName = URLEncoder.encode(originalName, StandardCharsets.UTF_8.displayName());
            } else {
                originalName = new String(originalName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            }
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + originalName);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
        } catch (Exception e) {
            System.err.println(String.format("下载文件:%s异常", originalName));
        }
    }

    /**
     * 获取文件流
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     */
    public static InputStream getObject(String bucketName, String objectName) throws Exception {
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 断点下载
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param offset     起始字节的位置
     * @param length     要读取的长度
     * @return 流
     */
    public InputStream getObject(String bucketName, String objectName, long offset, long length)
            throws Exception {
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(offset).length(length).build());
    }


    /**
     * 获取路径下文件列表
     *
     * @param bucketName bucket名称
     * @param prefix     文件名称
     * @param recursive  是否递归查找,如果是false,就模拟文件夹结构查找
     * @return 二进制流
     */
    public static Iterable> listObjects(String bucketName, String prefix, boolean recursive) {
        return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
    }

    /**
     * 获取路径下文件列表
     *
     * @param bucketName bucket名称
     * @param recursive  是否递归查找,如果是false,就模拟文件夹结构查找
     * @return 二进制流
     */
    public static Iterable> listObjects(String bucketName, boolean recursive) {
        return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).recursive(recursive).build());
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     */
    public static void removeObject(String bucketName, String objectName)
            throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 批量删除文件
     *
     * @param bucketName bucket
     * @param keys       需要删除的文件列表
     */
    public static void removeObjects(String bucketName, List keys) {
        List objects = new LinkedList<>();
        keys.forEach(s -> {
            objects.add(new DeleteObject(s));
            try {
                removeObject(bucketName, s);
            } catch (Exception e) {
                System.err.println("批量删除失败!");
            }
        });
    }

    /**
     * 生成预览链接,最大7天有效期;
     * 如果想永久有效,在 minio 控制台设置仓库访问规则总几率
     *
     * @param object      文件名称
     * @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf
     * @param validTime   有效时间 不能超过7天
     * @param timeUnit    单位 时 分 秒 天
     * @return java.lang.String
     **/
    public static String getPreviewUrl(String bucketName, String object, String contentType, Integer validTime, TimeUnit timeUnit) {
        Map reqParams = null;
        if (contentType != null){
            reqParams = new HashMap<>();
            reqParams.put("response-content-type", contentType != null ? contentType : "application/pdf");
        }
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(object)
                            .expiry(validTime, timeUnit)
                            .extraQueryParams(reqParams)
                            .build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * Description 文件列表
     *
     * @param limit 范围 1-1000
     **/
    public List listObjects(int limit, String bucketName) {
        List objects = new ArrayList<>();
        Iterable> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .maxKeys(limit)
                        .includeVersions(true)
                        .build());
        try {
            for (Result result : results) {
                objects.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objects;
    }

    /**
     * Description 网络文件转储 minio
     *
     * @param httpUrl 文件地址
     **/
    public static void netToMinio(String httpUrl, String bucketName) {
        int i = httpUrl.lastIndexOf(".");
        String substring = httpUrl.substring(i);
        URL url;
        try {
            url = new URL(httpUrl);
            URLConnection urlConnection = url.openConnection();
            // agent 模拟浏览器
            urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36");
            DataInputStream dataInputStream = new DataInputStream(url.openStream());

            // 临时文件转储
            File tempFile = File.createTempFile(UUID.randomUUID().toString().replace("-", ""), substring);
            FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = dataInputStream.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
            fileOutputStream.write(output.toByteArray());
            // 上传minio
            putObject(bucketName,tempFile.getAbsolutePath(), tempFile.getName());
            dataInputStream.close();
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Description 文件转字节数组
     *
     * @param path 文件路径
     * @return byte[] 字节数组
     **/
    public byte[] fileToBytes(String path) {
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            fis = new FileInputStream(path);
            int temp;
            byte[] bt = new byte[1024 * 10];
            while ((temp = fis.read(bt)) != -1) {
                bos.write(bt, 0, temp);
            }
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (Objects.nonNull(fis)) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bos.toByteArray();
    }

    /**
     * 将URLDecoder编码转成UTF8
     */
    public static String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {
        String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
        return URLDecoder.decode(url, "UTF-8");
    }
}


package com.zz.tiger.config.minio;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.zz.tiger.constant.CommonConst;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;

    /**
     * 查看存储bucket是否存在
     *
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            //e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶名称
     */
    @SneakyThrows
    public boolean makeBucket(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (!flag) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            return true;
        } else {
            return false;
        }
    }

    /**
     * 文件上传
     *
     * @param file 文件
     * @return Boolean
     */
    public String upload(MultipartFile file, String bucketName) {
        String fileName;
        try {
            String extName = FileUtil.extName(file.getOriginalFilename());
            fileName = IdUtil.simpleUUID() + CommonConst.DOT + extName;
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return fileName;
    }

    /**
     *
     * @param bucketName 桶名
     * @param file 文件
     * @param folderName 文件夹
     * @return
     */
    public Boolean upload(MultipartFile file,String folderName, String bucketName) {
        try {
            String extName = FileUtil.extName(file.getOriginalFilename());
            String uuid = IdUtil.simpleUUID();
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(folderName+File.separator+uuid+ CommonConst.DOT+extName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 预览图片
     *
     * @param fileName
     * @return
     */
    public String preview(String bucketName, String fileName) {
        // 查看文件地址
        GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     *
     * @param fileName 文件名称
     * @param res      response
     * @return Boolean
     */
    public void download(String bucketName, String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = response.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                //设置强制下载不打开
                //res.setContentType("application/force-download");
                res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                try (ServletOutputStream stream = res.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查看文件对象
     *
     * @return 存储bucket内文件对象信息
     */
    public List listObjects(String bucketName) {
        Iterable> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build()
        );
        List items = new ArrayList<>();
        try {
            for (Result result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String bucketName, String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 批量删除文件对象(没测试)
     *
     * @param objects 对象名称集合
     */
    public Iterable> removeObjects(String bucketName, List objects) {
        List dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
        return results;
    }


    /**
     * 列出所有存储桶名称
     *
     * @return
     */
    @SneakyThrows
    public List listBucketNames() {
        List bucketList = listBuckets();
        List bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }

    /**
     * 列出所有存储桶
     *
     * @return
     */
    @SneakyThrows
    public List listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 删除存储bucket
     *
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 列出存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @return
     */
    @SneakyThrows
    public Iterable> listObjects2(String bucketName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        }
        return null;
    }



    /**
     * 通过文件上传到对象
     *
     * @param bucketName 存储桶名称
     * @param
     * @param file       File
     * @return
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String objectName, MultipartFile file) {
        boolean flag = bucketExists(bucketName);
        if (flag) {

            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            minioClient.putObject(objectArgs);
            StatObjectResponse statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.size() > 0) {
                return true;
            }

        }
        return false;

    }

    /**
     * 获取对象的元数据
     *
     * @param bucketName 存储桶名称
     * @param filename   存储桶里的对象名称
     * @return
     */
    @SneakyThrows
    public StatObjectResponse statObject(String bucketName, String filename) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            /*  StatObjectArgs build =  StatObjectArgs.builder().bucket(bucketName).object(filename).build();*/
            StatObjectArgs build = StatObjectArgs.builder().bucket(bucketName).object(filename).build();
            StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(filename).build());
            /*  StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(filename).build());*/
            if (statObjectResponse == null) {

                return null;
            }

            return statObjectResponse;
        }
        return null;
    }

    /**
     * 通过InputStream上传对象
     *
     * @param bucketName 存储桶名称
     * @param filename   存储桶里的对象名称
     * @param stream     要上传的流
     * @return
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String filename, InputStream stream) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            PutObjectArgs stream1 = PutObjectArgs.builder().bucket(bucketName).object(filename).stream(stream, stream.available(), -1).build();
            minioClient.putObject(stream1);
            StatObjectResponse statObject = statObject(bucketName, filename);
            if (statObject != null && statObject.size() > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 以流的形式获取一个文件对象
     *
     * @param bucketName 存储桶名称
     * @param fileName   存储桶里的对象名称
     * @return
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String fileName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            StatObjectResponse statObject = statObject(bucketName, fileName);
            if (statObject != null && statObject.size() > 0) {
                InputStream stream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
                return stream;
            }
        }
        return null;
    }

    /**
     * 下载并将文件保存到本地
     *
     * @param bucketName 存储桶名称
     * @param bucketName 存储桶里的对象名称
     * @param fileName   File name
     * @return
     */
    @SneakyThrows
    public boolean getObject(String bucketName, String fileName, HttpServletResponse response) {
        boolean flag = bucketExists(bucketName);
        try {
            if (flag) {
                StatObjectResponse stat = minioClient.statObject(
                        StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
                response.setContentType(stat.contentType());
                response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "utf-8"));
                StatObjectResponse statObject = statObject(bucketName, fileName);
                if (statObject != null && statObject.size() > 0) {
                    GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
                    IOUtils.copy(object, response.getOutputStream());
                    object.close();
                    return true;
                }
            }
        } catch (Exception e) {
            e.getMessage();
        }
        return false;
    }

    /**
     * 删除一个对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     */
    @SneakyThrows
    public boolean removeObject(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        if (flag) {

            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return true;
        }
        return false;
    }

    /**
     * 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表
     *
     * @param bucketName  存储桶名称
     * @param objectNames 含有要删除的多个object名称的迭代器对象
     * @return
     */
    @SneakyThrows
    public List removeObject(String bucketName, List objectNames) {
        List deleteErrorNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {

            /*   Iterable> results = minioClient.removeObjects(bucketName, objectNames);*/
            List deleteObjectList = new CopyOnWriteArrayList<>();
            for (String objectName : objectNames) {
                deleteErrorNames.add(objectName);
            }

            Iterable> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjectList).build());
            for (Result result : results) {
                DeleteError error = result.get();
                deleteErrorNames.add(error.objectName());
            }


        }
        return deleteErrorNames;
    }

    /**
     * 生成一个给HTTP GET请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
     * 如果想永久有效,在 minio 控制台设置仓库访问规则总几率
     * @param object      文件名称
     * @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf
     * @param expires   有效时间 不能超过7天
     * @param timeUnit    单位 时 分 秒 天
     * @return java.lang.String
     **/
    public String presignedGetObject(String bucketName, String object, String contentType, Integer expires, TimeUnit timeUnit) {
        Map reqParams = null;
        if (contentType != null){
            reqParams = new HashMap<>();
            reqParams.put("response-content-type", contentType != null ? contentType : "application/pdf");
        }
        String url = null;
        try {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new Exception(expires +
                        "过期时间范围内必须在1到 " + DEFAULT_EXPIRY_TIME);
            }
            url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(object)
                            .expiry(expires, timeUnit)
                            .extraQueryParams(reqParams)
                            .build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * 生成一个给HTTP PUT请求用的presigned URL。
     * 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
     * 如果想永久有效,在 minio 控制台设置仓库访问规则总几率
     * @param object      文件名称
     * @param contentType 预览类型 image/gif", "image/jpeg", "image/jpg", "image/png", "application/pdf
     * @param expires   有效时间 不能超过7天
     * @param timeUnit    单位 时 分 秒 天
     * @return java.lang.String
     **/
    @SneakyThrows
    public String presignedPutObject(String bucketName, String object, String contentType, Integer expires, TimeUnit timeUnit) {
        Map reqParams = null;
        if (contentType != null){
            reqParams = new HashMap<>();
            reqParams.put("response-content-type", contentType != null ? contentType : "application/pdf");
        }
        String url = null;
        try {
            if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
                throw new Exception(expires +
                        "过期时间范围内必须在1到 " + DEFAULT_EXPIRY_TIME);
            }
            url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.PUT)
                            .bucket(bucketName)
                            .object(object)
                            .expiry(expires, timeUnit)
                            .extraQueryParams(reqParams)
                            .build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }


    /**
     * 文件访问路径
     *
     * @param bucketName 存储桶名称
     * @param objectName 存储桶里的对象名称
     * @return
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
//            url = minioClient.getObjectUrl(bucketName, objectName);
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).build());
        }
        return url;
    }

    /**
     * 下载文件2
     *
     * @param bucketName
     * @param fileName     在桶里面的文件名称
     * @param originalName 展示的文件名称
     * @param response
     */
    public void downloadFile(String bucketName, String fileName, String originalName, HttpServletResponse response) {
        try {

            InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());

            String filename = new String(fileName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
            if (StringUtils.isNotEmpty(originalName)) {
                fileName = originalName;
            }
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取对应的contentType
    private static Map map = new HashMap<>();

    static {
        map.put("other", "application/octet-stream");
        map.put(".tif", "image/tiff");
        map.put("0.001", "application/x-001");
        map.put("0.301", "application/x-301");
        map.put("0.323", "text/h323");
        map.put("0.906", "application/x-906");
        map.put("0.907", "drawing/907");
        map.put(".a11", "application/x-a11");
        map.put(".acp", "audio/x-mei-aac");
        map.put(".ai", "application/postscript");
        map.put(".aif", "audio/aiff");
        map.put(".aifc", "audio/aiff");
        map.put(".aiff", "audio/aiff");
        map.put(".anv", "application/x-anv");
        map.put(".asa", "text/asa");
        map.put(".asf", "video/x-ms-asf");
        map.put(".asp", "text/asp");
        map.put(".asx", "video/x-ms-asf");
        map.put(".au", "audio/basic");
        map.put(".avi", "video/avi");
        map.put(".awf", "application/vnd.adobe.workflow");
        map.put(".biz", "text/xml");
        map.put(".bmp", "application/x-bmp");
        map.put(".bot", "application/x-bot");
        map.put(".c4t", "application/x-c4t");
        map.put(".c90", "application/x-c90");
        map.put(".cal", "application/x-cals");
        map.put(".cat", "application/vnd.ms-pki.seccat");
        map.put(".cdf", "application/x-netcdf");
        map.put(".cdr", "application/x-cdr");
        map.put(".cel", "application/x-cel");
        map.put(".cer", "application/x-x509-ca-cert");
        map.put(".cg4", "application/x-g4");
        map.put(".cgm", "application/x-cgm");
        map.put(".cit", "application/x-cit");
        map.put(".class", "java/");
        map.put(".cml", "text/xml");
        map.put(".cmp", "application/x-cmp");
        map.put(".cmx", "application/x-cmx");
        map.put(".cot", "application/x-cot");
        map.put(".crl", "application/pkix-crl");
        map.put(".crt", "application/x-x509-ca-cert");
        map.put(".csi", "application/x-csi");
        map.put(".css", "text/css");
        map.put(".cut", "application/x-cut");
        map.put(".dbf", "application/x-dbf");
        map.put(".dbm", "application/x-dbm");
        map.put(".dbx", "application/x-dbx");
        map.put(".dcd", "text/xml");
        map.put(".dcx", "application/x-dcx");
        map.put(".der", "application/x-x509-ca-cert");
        map.put(".dgn", "application/x-dgn");
        map.put(".dib", "application/x-dib");
        map.put(".dll", "application/x-msdownload");
        map.put(".doc", "application/msword");
        map.put(".dot", "application/msword");
        map.put(".drw", "application/x-drw");
        map.put(".dtd", "text/xml");
        map.put(".dwf", "Model/vnd.dwf");
        //map.put(".dwf","application/x-dwf");
        map.put(".dwg", "application/x-dwg");
        map.put(".dxb", "application/x-dxb");
        map.put(".dxf", "application/x-dxf");
        map.put(".edn", "application/vnd.adobe.edn");
        map.put(".emf", "application/x-emf");
        map.put(".eml", "message/rfc822");
        map.put(".ent", "text/xml");
        map.put(".epi", "application/x-epi");
        map.put(".eps", "application/x-ps");
        //map.put(".eps","application/postscript");
        map.put(".etd", "application/x-ebx");
        map.put(".exe", "application/x-msdownload");
        map.put(".fax", "image/fax");
        map.put(".fdf", "application/vnd.fdf");
        map.put(".fif", "application/fractals");
        map.put(".fo", "text/xml");
        map.put(".frm", "application/x-frm");
        map.put(".g4", "application/x-g4");
        map.put(".gbr", "application/x-gbr");
        map.put(".", "application/x-");
        map.put(".gif", "image/gif");
        map.put(".gl2", "application/x-gl2");
        map.put(".gp4", "application/x-gp4");
        map.put(".hgl", "application/x-hgl");
        map.put(".hmr", "application/x-hmr");
        map.put(".hpg", "application/x-hpgl");
        map.put(".hpl", "application/x-hpl");
        map.put(".hqx", "application/mac-binhex40");
        map.put(".hrf", "application/x-hrf");
        map.put(".hta", "application/hta");
        map.put(".htc", "text/x-component");
        map.put(".htm", "text/html");
        map.put(".html", "text/html");
        map.put(".htt", "text/webviewhtml");
        map.put(".htx", "text/html");
        map.put(".icb", "application/x-icb");
        map.put(".ico", "image/x-icon");
        //map.put(".ico","application/x-ico");
        map.put(".iff", "application/x-iff");
        map.put(".ig4", "application/x-g4");
        map.put(".igs", "application/x-igs");
        map.put(".iii", "application/x-iphone");
        map.put(".img", "application/x-img");
        map.put(".ins", "application/x-internet-signup");
        map.put(".isp", "application/x-internet-signup");
        map.put(".IVF", "video/x-ivf");
        map.put(".java", "java/*");
        map.put(".jfif", "image/jpeg");
        map.put(".jpe", "image/jpeg");
        //map.put(".jpe","application/x-jpe");
        map.put(".jpeg", "image/jpeg");
        map.put(".jpg", "image/jpeg");
        //map.put(".jpg","application/x-jpg");
        map.put(".js", "application/x-javascript");
        map.put(".jsp", "text/html");
        map.put(".la1", "audio/x-liquid-file");
        map.put(".lar", "application/x-laplayer-reg");
        map.put(".latex", "application/x-latex");
        map.put(".lavs", "audio/x-liquid-secure");
        map.put(".lbm", "application/x-lbm");
        map.put(".lmsff", "audio/x-la-lms");
        map.put(".ls", "application/x-javascript");
        map.put(".ltr", "application/x-ltr");
        map.put(".m1v", "video/x-mpeg");
        map.put(".m2v", "video/x-mpeg");
        map.put(".m3u", "audio/mpegurl");
        map.put(".m4e", "video/mpeg4");
        map.put(".mac", "application/x-mac");
        map.put(".man", "application/x-troff-man");
        map.put(".math", "text/xml");
        map.put(".mdb", "application/msaccess");
        //map.put(".mdb","application/x-mdb");
        map.put(".mfp", "application/x-shockwave-flash");
        map.put(".mht", "message/rfc822");
        map.put(".mhtml", "message/rfc822");
        map.put(".mi", "application/x-mi");
        map.put(".mid", "audio/mid");
        map.put(".midi", "audio/mid");
        map.put(".mil", "application/x-mil");
        map.put(".mml", "text/xml");
        map.put(".mnd", "audio/x-musicnet-download");
        map.put(".mns", "audio/x-musicnet-stream");
        map.put(".mocha", "application/x-javascript");
        map.put(".movie", "video/x-sgi-movie");
        map.put(".mp1", "audio/mp1");
        map.put(".mp2", "audio/mp2");
        map.put(".mp2v", "video/mpeg");
        map.put(".mp3", "audio/mp3");
        map.put(".aac", "audio/mp3");
        map.put(".mp4", "video/mpeg4");
        map.put(".mpa", "video/x-mpg");
        map.put(".mpd", "application/vnd.ms-project");
        map.put(".mpe", "video/x-mpeg");
        map.put(".mpeg", "video/mpg");
        map.put(".mpg", "video/mpg");
        map.put(".mpga", "audio/rn-mpeg");
        map.put(".mpp", "application/vnd.ms-project");
        map.put(".mps", "video/x-mpeg");
        map.put(".mpt", "application/vnd.ms-project");
        map.put(".mpv", "video/mpg");
        map.put(".mpv2", "video/mpeg");
        map.put(".mpw", "application/vnd.ms-project");
        map.put(".mpx", "application/vnd.ms-project");
        map.put(".mtx", "text/xml");
        map.put(".mxp", "application/x-mmxp");
        map.put(".net", "image/pnetvue");
        map.put(".nrf", "application/x-nrf");
        map.put(".nws", "message/rfc822");
        map.put(".odc", "text/x-ms-odc");
        map.put(".out", "application/x-out");
        map.put(".p10", "application/pkcs10");
        map.put(".p12", "application/x-pkcs12");
        map.put(".p7b", "application/x-pkcs7-certificates");
        map.put(".p7c", "application/pkcs7-mime");
        map.put(".p7m", "application/pkcs7-mime");
        map.put(".p7r", "application/x-pkcs7-certreqresp");
        map.put(".p7s", "application/pkcs7-signature");
        map.put(".pc5", "application/x-pc5");
        map.put(".pci", "application/x-pci");
        map.put(".pcl", "application/x-pcl");
        map.put(".pcx", "application/x-pcx");
        map.put(".pdf", "application/pdf");
        //map.put(".pdf","application/pdf");
        map.put(".pdx", "application/vnd.adobe.pdx");
        map.put(".pfx", "application/x-pkcs12");
        map.put(".pgl", "application/x-pgl");
        map.put(".pic", "application/x-pic");
        map.put(".pko", "application/vnd.ms-pki.pko");
        map.put(".pl", "application/x-perl");
        map.put(".plg", "text/html");
        map.put(".pls", "audio/scpls");
        map.put(".plt", "application/x-plt");
        map.put(".png", "image/png");
        //map.put(".png","application/x-png");
        map.put(".pot", "application/vnd.ms-powerpoint");
        map.put(".ppa", "application/vnd.ms-powerpoint");
        map.put(".ppm", "application/x-ppm");
        map.put(".pps", "application/vnd.ms-powerpoint");
        map.put(".ppt", "application/vnd.ms-powerpoint");
        //map.put(".ppt","application/x-ppt");
        map.put(".pr", "application/x-pr");
        map.put(".prf", "application/pics-rules");
        map.put(".prn", "application/x-prn");
        map.put(".prt", "application/x-prt");
        map.put(".ps", "application/x-ps");
        //map.put(".ps","application/postscript");
        map.put(".ptn", "application/x-ptn");
        map.put(".pwz", "application/vnd.ms-powerpoint");
        map.put(".r3t", "text/vnd.rn-realtext3d");
        map.put(".ra", "audio/vnd.rn-realaudio");
        map.put(".ram", "audio/x-pn-realaudio");
        map.put(".ras", "application/x-ras");
        map.put(".rat", "application/rat-file");
        map.put(".rdf", "text/xml");
        map.put(".rec", "application/vnd.rn-recording");
        map.put(".red", "application/x-red");
        map.put(".rgb", "application/x-rgb");
        map.put(".rjs", "application/vnd.rn-realsystem-rjs");
        map.put(".rjt", "application/vnd.rn-realsystem-rjt");
        map.put(".rlc", "application/x-rlc");
        map.put(".rle", "application/x-rle");
        map.put(".rm", "application/vnd.rn-realmedia");
        map.put(".rmf", "application/vnd.adobe.rmf");
        map.put(".rmi", "audio/mid");
        map.put(".rmj", "application/vnd.rn-realsystem-rmj");
        map.put(".rmm", "audio/x-pn-realaudio");
        map.put(".rmp", "application/vnd.rn-rn_music_package");
        map.put(".rms", "application/vnd.rn-realmedia-secure");
        map.put(".rmvb", "application/vnd.rn-realmedia-vbr");
        map.put(".rmx", "application/vnd.rn-realsystem-rmx");
        map.put(".rnx", "application/vnd.rn-realplayer");
        map.put(".rp", "image/vnd.rn-realpix");
        map.put(".rpm", "audio/x-pn-realaudio-plugin");
        map.put(".rsml", "application/vnd.rn-rsml");
        map.put(".rt", "text/vnd.rn-realtext");
        map.put(".rtf", "application/msword");
        //map.put(".rtf","application/x-rtf");
        map.put(".rv", "video/vnd.rn-realvideo");
        map.put(".sam", "application/x-sam");
        map.put(".sat", "application/x-sat");
        map.put(".sdp", "application/sdp");
        map.put(".sdw", "application/x-sdw");
        map.put(".sit", "application/x-stuffit");
        map.put(".slb", "application/x-slb");
        map.put(".sld", "application/x-sld");
        map.put(".slk", "drawing/x-slk");
        map.put(".smi", "application/smil");
        map.put(".smil", "application/smil");
        map.put(".smk", "application/x-smk");
        map.put(".snd", "audio/basic");
        map.put(".sol", "text/plain");
        map.put(".sor", "text/plain");
        map.put(".spc", "application/x-pkcs7-certificates");
        map.put(".spl", "application/futuresplash");
        map.put(".spp", "text/xml");
        map.put(".ssm", "application/streamingmedia");
        map.put(".sst", "application/vnd.ms-pki.certstore");
        map.put(".stl", "application/vnd.ms-pki.stl");
        map.put(".stm", "text/html");
        map.put(".sty", "application/x-sty");
        map.put(".svg", "text/xml");
        map.put(".swf", "application/x-shockwave-flash");
        map.put(".tdf", "application/x-tdf");
        map.put(".tg4", "application/x-tg4");
        map.put(".tga", "application/x-tga");
        //map.put(".tif","image/tiff");
        //map.put(".tif","application/x-tif");
        map.put(".tiff", "image/tiff");
        map.put(".tld", "text/xml");
        map.put(".top", "drawing/x-top");
        map.put(".torrent", "application/x-bittorrent");
        map.put(".tsd", "text/xml");
        map.put(".txt", "text/plain");
        map.put(".uin", "application/x-icq");
        map.put(".uls", "text/iuls");
        map.put(".vcf", "text/x-vcard");
        map.put(".vda", "application/x-vda");
        map.put(".vdx", "application/vnd.visio");
        map.put(".vml", "text/xml");
        map.put(".vpg", "application/x-vpeg005");
        map.put(".vsd", "application/vnd.visio");
        //map.put(".vsd","application/x-vsd");
        map.put(".vss", "application/vnd.visio");
        map.put(".vst", "application/vnd.visio");
        //map.put(".vst","application/x-vst");
        map.put(".vsw", "application/vnd.visio");
        map.put(".vsx", "application/vnd.visio");
        map.put(".vtx", "application/vnd.visio");
        map.put(".vxml", "text/xml");
        map.put(".wav", "audio/wav");
        map.put(".wax", "audio/x-ms-wax");
        map.put(".wb1", "application/x-wb1");
        map.put(".wb2", "application/x-wb2");
        map.put(".wb3", "application/x-wb3");
        map.put(".wbmp", "image/vnd.wap.wbmp");
        map.put(".wiz", "application/msword");
        map.put(".wk3", "application/x-wk3");
        map.put(".wk4", "application/x-wk4");
        map.put(".wkq", "application/x-wkq");
        map.put(".wks", "application/x-wks");
        map.put(".wm", "video/x-ms-wm");
        map.put(".wma", "audio/x-ms-wma");
        map.put(".wmd", "application/x-ms-wmd");
        map.put(".wmf", "application/x-wmf");
        map.put(".wml", "text/vnd.wap.wml");
        map.put(".wmv", "video/x-ms-wmv");
        map.put(".wmx", "video/x-ms-wmx");
        map.put(".wmz", "application/x-ms-wmz");
        map.put(".wp6", "application/x-wp6");
        map.put(".wpd", "application/x-wpd");
        map.put(".wpg", "application/x-wpg");
        map.put(".wpl", "application/vnd.ms-wpl");
        map.put(".wq1", "application/x-wq1");
        map.put(".wr1", "application/x-wr1");
        map.put(".wri", "application/x-wri");
        map.put(".wrk", "application/x-wrk");
        map.put(".ws", "application/x-ws");
        map.put(".ws2", "application/x-ws");
        map.put(".wsc", "text/scriptlet");
        map.put(".wsdl", "text/xml");
        map.put(".wvx", "video/x-ms-wvx");
        map.put(".xdp", "application/vnd.adobe.xdp");
        map.put(".xdr", "text/xml");
        map.put(".xfd", "application/vnd.adobe.xfd");
        map.put(".xfdf", "application/vnd.adobe.xfdf");
        map.put(".xhtml", "text/html");
        map.put(".xls", "application/vnd.ms-excel");
        //map.put(".xls","application/x-xls");
        map.put(".xlw", "application/x-xlw");
        map.put(".xml", "text/xml");
        map.put(".xpl", "audio/scpls");
        map.put(".xq", "text/xml");
        map.put(".xql", "text/xml");
        map.put(".xquery", "text/xml");
        map.put(".xsd", "text/xml");
        map.put(".xsl", "text/xml");
        map.put(".xslt", "text/xml");
        map.put(".xwd", "application/x-xwd");
        map.put(".x_b", "application/x-x_b");
        map.put(".sis", "application/vnd.symbian.install");
        map.put(".sisx", "application/vnd.symbian.install");
        map.put(".x_t", "application/x-x_t");
        map.put(".ipa", "application/vnd.iphone");
        map.put(".apk", "application/vnd.android.package-archive");
        map.put(".xap", "application/x-silverlight-app");
    }

}


你可能感兴趣的:(java,开发语言)