vaadin23集成minio实现文件上传下载

1.添加依赖

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

2.注册minio客户端bean

 @Resource
    private MinioConfig ossProperties;
 
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(ossProperties.getEndpoint())
                .credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey())
                .build();
    }
参数配置类
/**
 * Minio参数配置类
 *
 * @author meng
 */
@Component
@ConfigurationProperties(prefix = MinioConfig.PREFIX)
public class MinioConfig {
 
	/**
	 * 配置前缀
	 */
	public static final String PREFIX = "oss";
 
	/**
	 * 对象存储名称
	 */
	private String name;
 
	/**
	 * 对象存储服务的URL
	 */
	private String endpoint;
 
	/**
	 * Access key 账户ID
	 */
	private String accessKey;
 
	/**
	 * Secret key 密码
	 */
	private String secretKey;
 
	/**
	 * 默认的存储桶名称
	 */
	private String bucketName = "meng";
 
	/**
	 * 可上传的文件后缀名
	 */
	private List<String> fileExt;

3.添加配置文件

oss.access-key=minioadmin
oss.secret-key=minioadmin
oss.bucket-name=test
oss.folder=chat
oss.endpoint=http://192.168.190.100:9000
oss.file-ext=png,jpg,jpeg,pdf,doc,docx,xls,xlsx

4.编写工具类

@Component
public class MinioUtils {

	
	private static final Logger log = LoggerFactory.getLogger(MinioUtils.class);

	/**
	 * 字符集
	 */
	private static final String ENCODING = "UTF-8";
	  @Autowired
	    public  void setMinioClient(MinioClient minioClient) {MinioUtils.minioClient = minioClient;}
	    @Value("${oss.bucket-name}")
	    public  void setBucketName(String bucketName) {
	        MinioUtils.bucketName = bucketName;
	    }
	    @Value("${oss.endpoint}")
	    public  void setEndpoint(String endpoint) {
	        MinioUtils.endpoint = endpoint;
	    }
	    @Value("${oss.folder}")
	    public  void setFolder(String folder) {
	        MinioUtils.folder = folder;
	    }
	    @Value("${oss.access-key}")
	    public  void setAccessKey(String accessKey) {
	        MinioUtils.accessKey = accessKey;
	    }
	    @Value("${oss.secret-key}")
	    public  void setSecretKey(String secretKey) {
	        MinioUtils.secretKey = secretKey;
	    }
	    private static MinioClient minioClient;
	    private static String bucketName;

	    private static String endpoint;

	    private static String folder;

	    private static String accessKey;

	    private static String secretKey;

	    
    public static boolean  bucketExists(String bucketName) {
		try {
			return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
		} catch (Exception e) {
			log.error("minio bucketExists Exception:{}", e);
		}
		return false;
	}

	/**
	 * @Description: 创建 存储桶
	 * @Param bucketName: 存储桶名称
	 * @return: void
	 * @Date: 2023/8/2 11:28
	 */
	public static void makeBucket(String bucketName) {
		try {
			if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
				minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
				log.info("minio makeBucket success bucketName:{}", bucketName);
			}
		} catch (Exception e) {
			log.error("minio makeBucket Exception:{}", e);
		}
	}
    /**
     * 上传File文件
     *
     * @param file
     * @return
     */
    public static String uploadFile(File file) {
        MultipartFile multipartFile = fileToMultipartFile(file);
        return uploadMultipartFile(multipartFile,minioClient,endpoint,bucketName,folder);
    }

    /**
     * 上传multipartFile文件
     * @param multipartFile
     * @return
     */
    public static String uploadFile(MultipartFile multipartFile) {
        return uploadMultipartFile(multipartFile,minioClient,endpoint,bucketName,folder);
    }

    /**
     * 上传MultipartFile文件
     * @param file
     * @param minioClient
     * @param endpoint
     * @param bucketName
     * @param folder
     * @return
     */
    private static String uploadMultipartFile(MultipartFile file, MinioClient minioClient, String endpoint, String bucketName, String folder) {
        //判断bucket是否存在
        existBucket(minioClient, bucketName);
        //判断目录路径是否存在
        if (!doesFolderExist(minioClient,bucketName, folder)) {
            try {
                createFolder(minioClient,bucketName, folder);
            } catch (Exception e) {
                //throw new RuntimeException(e);
                log.error("创建目录失败!");
            }
        }
        String fileName = file.getOriginalFilename();

        String[] split = fileName.split("\\.");
        if (split.length > 1) {
            fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
        } else {
            fileName = fileName + System.currentTimeMillis();
        }
        String objectName = folder +StrUtil.C_SLASH + fileName;
        //log.debug("objectName是路径,文件最终整合的路径的值为:{}", objectName);
        InputStream in = null;
        try {
            in = file.getInputStream();
            //此方法为动态获取contentType,可以直接预览
            String contentType = getContentTypeByFileName(fileName);
            log.info("contentType的值为:{}", contentType);
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(in, in.available(), -1)
                    //如果为流,默认不预览直接下载
                    //.contentType("application/octet-stream")
                    .contentType(contentType)
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("文件上传失败"+e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //String url=endpoint + "/" + bucketName + "/" + objectName;
        String url=endpoint + "/" + bucketName + "/" + objectName;
        log.info("上传minIo后文件url的值为:{}", url);
        return url;
    }

    /**
     * 根据文件名称下载File类型的文件
     *
     * @param fileUrl
     * @return
     */
    public MultipartFile downloadMultipartFileByUrl(String fileUrl) {

        try {
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            MultipartFile multipartFile = getMultipartFile(inputStream, fileName);
            return multipartFile;
        } catch (Exception e) {
            log.error("文件下载失败:{}", e.getMessage());
            throw  new RuntimeException("文件下载失败"+e.getMessage());
        }
    }
    /**
	 * @Description: 下载文件
	 * @Param response: 响应
	 * @Param fileName: 文件名
	 * @Param filePath: 文件路径
	 * @return: void
	 * @Date: 2023/8/2 14:08
	 */
    public static void downloadFile(HttpServletResponse response, String fileName, String filePath) {
		GetObjectResponse is = null;
		try {
			GetObjectArgs getObjectArgs = GetObjectArgs.builder().bucket(bucketName).object(filePath)
					.build();
			is = minioClient.getObject(getObjectArgs);
			// 设置文件ContentType类型,这样设置,会自动判断下载文件类型
			response.setContentType("application/x-msdownload");
			response.setCharacterEncoding(ENCODING);
			// 设置文件头:最后一个参数是设置下载的文件名并编码为UTF-8
			response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, ENCODING));
			IoUtil.copy(is, response.getOutputStream());
			log.info("minio downloadFile success, filePath:{}", filePath);
		} catch (Exception e) {
			log.error("minio downloadFile Exception:{}", e);
		} finally {
			IoUtil.close(is);
		}
	}

    /**
     * 根据文件名称下载File类型的文件
     *
     * @param fileUrl
     * @return
     */
    public File downloadFileByUrl(String fileUrl) {
        try {
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            MultipartFile multipartFile = getMultipartFile(inputStream, fileName);
            File file = MultipartFileToFile(multipartFile);
            return file;

        } catch (Exception e) {
            log.error("文件下载失败:{}", e.getMessage());
            throw  new RuntimeException("文件下载失败"+e.getMessage());
        }
    }
    
    public static MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
        FileItem fileItem = createFileItem(inputStream, fileName);
        //CommonsMultipartFile是feign对multipartFile的封装,但是要FileItem类对象
        return new CommonsMultipartFile(fileItem);
    }


    /**
     * FileItem类对象创建
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return FileItem
     */
    public static FileItem createFileItem(InputStream inputStream, String fileName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        String textFieldName = "file";
        FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
        int bytesRead = 0;
        byte[] buffer = new byte[10 * 1024 * 1024];
        OutputStream os = null;
        //使用输出流输出输入流的字节
        try {
            os = item.getOutputStream();
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            inputStream.close();
        } catch (IOException e) {
            log.error("Stream copy exception", e);
            throw new IllegalArgumentException("文件上传失败");
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error("Stream close exception", e);

                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("Stream close exception", e);
                }
            }
        }
        return item;
    }


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

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

    /**
     * 获取文件外链
     *
     * @param objectName 文件名称
     * @param expires    过期时间 <=7天 秒级
     * @return url
     */
    public static String getPresignedObjectUrl(String objectName,
                                        Integer expires) {
        try {
            String fileUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET)
					.bucket(bucketName).object(objectName).expiry(expires).build());
            return fileUrl;
        } catch (Exception e) {
            log.error("获取文件外链失败:{}", e.getMessage());
            throw  new RuntimeException("获取文件外链失败:{}"+e.getMessage());
        }
    }

    /**
     * 给presigned URL设置策略
     * 允许给POST请求的presigned URL设置策略,比如接收对象上传的存储桶名称的策略,key名称前缀,过期策略。
     *
     * @param objectName 对象名
     * @return map
     */
    public Map<String, String> presignedGetObject(String objectName) {
        try {
        	PostPolicy postPolicy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7));
            Map<String, String> formData = minioClient.getPresignedPostFormData(postPolicy);
            log.info("curl -X POST ");
            for (Map.Entry<String, String> entry : formData.entrySet()) {
                log.info(" -F " + entry.getKey() + "=" + entry.getValue());
            }
            return formData;
        } catch (Exception e) {
            log.error("Error occurred: " + e);
        }
        return null;
    }

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

    /**
     * 根据key删除服务器上的文件
     */
    public void deleteFileByUrl(String fileUrl) {
        try {
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(folder + fileName).build());
            log.info("删除" + bucketName + "下的文件" + folder + fileName + "成功");
        } catch (Exception e) {
            log.error("删除文件失败:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }
    
    /**
	 * 获取文件信息
	 * @param fileName 存储桶文件名称
	 * @return InputStream
	 */
	public static OssFile getOssInfo(String fileName) {
		try {
			StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
					.bucket(bucketName).object(fileName).build());
			OssFile ossFile = new OssFile();
			ossFile.setName(ObjectUtil.isEmpty(stat.object()) ? fileName : stat.object());
			ossFile.setFilePath(ossFile.getName());
			ossFile.setDomain(endpoint);
			ossFile.setHash(String.valueOf(stat.hashCode()));
			ossFile.setSize(stat.size());
			ossFile.setPutTime(DateUtil.date(stat.lastModified().toLocalDateTime()));
			ossFile.setContentType(stat.contentType());
			return ossFile;
		} catch (Exception e) {
			log.error("minio getOssInfo Exception:{}", e);
		}
		return null;
	}

    /**
     * 创建文件夹或目录
     *
     * @param bucketName 存储桶
     * @param objectName 目录路径
     */
    private static ObjectWriteResponse createFolder(MinioClient minioClient,String bucketName, String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                                new ByteArrayInputStream(new byte[]{}), 0, -1)
                        .build());
    }

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

    /**
     * 创建minioClient
     */
    private static MinioClient createMinioClient(MinioClient minioClient,String accessKey,String secretKey,String endpoint,String bucketName) {
        try {
            if (null == minioClient) {
                minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey)
                        .build();
                createBucket(minioClient,bucketName);
                //设置全局访问策略
                setBucketPolicy(minioClient,bucketName);
            } else {
                //设置全局访问策略
                setBucketPolicy(minioClient,bucketName);
            }
        } catch (Exception e) {
            log.error("初始化MinIO服务器异常:{}", e);
        }
        return minioClient;
    }

    private static void setBucketPolicy(MinioClient minioClient, String bucketName) {
        try {
            String config = "{\n" +
                    "     \"Statement\": [\n" +
                    "         {\n" +
                    "             \"Action\": [\n" +
                    "                 \"s3:GetBucketLocation\",\n" +
                    "                 \"s3:ListBucket\"\n" +
                    "             ],\n" +
                    "             \"Effect\": \"Allow\",\n" +
                    "             \"Principal\": \"*\",\n" +
                    "             \"Resource\": \"arn:aws:s3:::" + bucketName + "\"\n" +
                    "         },\n" +
                    "         {\n" +
                    "       \"Action\": [\n" +
                    "                \"s3:GetObject\",\n" +
                    "                \"s3:PutObject\",\n" +
                    "                \"s3:DeleteObject\"\n" +
                    "      ],\n" +
                    "             \"Effect\": \"Allow\",\n" +
                    "             \"Principal\": {\"AWS\":[\"*\"]},\n" +
                    "             \"Resource\": \"arn:aws:s3:::" + bucketName + "/" + "**" + "\"\n" +
                    "         }\n" +
                    "     ],\n" +
                    "     \"Version\": \"2012-10-17\"\n" +
                    "}";

            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(config).build());

        } catch (ErrorResponseException | InsufficientDataException
                 | InternalException | InvalidKeyException
                 | InvalidResponseException | IOException
                 | NoSuchAlgorithmException | ServerException
                 | XmlParserException e) {
            log.error("minio设置桶:{}策略失败", bucketName, e);
        }
    }

    /**
     * description: 判断bucket是否存在,不存在则创建
     *
     * @return: void
     */
    private static void existBucket(MinioClient minioClient,String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化Bucket
     *
     * @throws Exception 异常
     */
    private static void createBucket(MinioClient minioClient,String bucketName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    @PostConstruct
    private void handlerInit() {
        createMinioClient(minioClient,accessKey,secretKey,endpoint,bucketName);
        log.info("MinIo初始化成功");
    }

    /**
     * 、
     * 根据文件后缀名获取contentType
     */
    public static String getContentTypeByFileName(String fileName) {
        //此方法可以替代spring中根据文件后缀名获取contentType
        Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
        String contentType = mediaType.map(MediaType::toString).orElseGet(() -> MediaType.APPLICATION_OCTET_STREAM.toString());
        return contentType;
    }

    /**
     * File转换为MultipartFile
     *
     * @param file
     * @return
     */
    public static MultipartFile fileToMultipartFile(File file) {
        FileItem item = new DiskFileItemFactory().createItem("file"
                , MediaType.MULTIPART_FORM_DATA_VALUE
                , true
                , file.getName());
        try (InputStream input = new FileInputStream(file);
             OutputStream os = item.getOutputStream()) {
            // 流转移
            IOUtils.copy(input, os);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid file: " + e, e);
        }

        return new CommonsMultipartFile(item);
    }
    /**
     * 将MultipartFile转换为File
     *
     * @param multiFile
     * @return
     */
    public static File MultipartFileToFile(MultipartFile multiFile) {
        // 获取文件名
        String fileName = multiFile.getOriginalFilename();
        fileName = "tmp-" + fileName;
        // 获取文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        // 若须要防止生成的临时文件重复,能够在文件名后添加随机码
        try {
            //File file = File.createTempFile(FileUtil.getFileNameNotPrefix(fileName), suffix);
            File file = File.createTempFile(fileName.substring(0, fileName.lastIndexOf(".")), suffix);
            multiFile.transferTo(file);
            return file;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * MultipartFile 保存本地
     *
     * @param file
     * @param path
     * @return
     */
    public static String downloadFile(MultipartFile file, String path) {
        String fileName = file.getOriginalFilename();
        File dest = new File(new File(path).getAbsolutePath() + "/" + fileName);
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        try {
            file.transferTo(dest); // 保存文件
        } catch (Exception e) {
            e.printStackTrace();
        }
        return path + "/" + fileName;
    }
}

5.上传文件

MinioUtils.uploadFile(fileUploaded);

6.下载文件

MinioUtil.downloadFileByUrl()

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值