springboot最新minio实用javaAPI教程-8.0.x

前言:公司要做一个兼容本地存储和minio存储并可配置的静态资源保存,资源包括网站的图片和文档等

minio以前是用的6.0的jar包  但是这次从网上下载的minio的服务端是最新版,服务端启动之后有两个两个端口号,  api的9000  和  console的动态端口号(具体的安装教程网上一大把,这里要说的是你如果用的是APi调用的接口,比如说java调用,  就需要使用9000这个端口)  所以 6.0版本的有点不太适用,所以改成8.0的jar包,没有仔细去研究版本升级之后的差异点,但是感觉8.0版本 1是规范了api的参数  ,针对不同的api参数使用了内部类的模式可以链式调用,变成各种args.build().bucket().build,还有一些可以为null的参数,现在也必须有值,比如putObject中的ObjectSize 。

话不多说  ,上代码吧

配置文件

1 本地存储

@Service
@ConditionalOnProperty(value = {"file.store.type"},havingValue = "local")
public class MdFileStoreServiceImpl implements ObjectStoreService {
    private static final Logger logger = LoggerFactory.getLogger(MdFileStoreServiceImpl.class);

    @Value("${file.absolutepath}")
    private String absolutePath;

    @Value("${file.imageUrlPrefix}")
    private String imageUrlPrefix;

    @Resource
    private MdFileStoreInfoMapper mdFileStoreInfoMapper;

    

    @Override
    public String uploadFile(String bucket, String fileName, InputStream in, Long size, String mime, Boolean autoMatch) throws ServiceException {
        return null;
    }

    @Override
    public void downloadFile(String bucket, String remoteFileName, String localFileName) throws ServiceException {

    }

    @Override
    public String getDownloadLink(String bucket, String fileName, Integer expires) throws ServiceException {
        return null;
    }


    @Override
    public void batchDeleteFile(String bucket, String prefix) throws ServiceException {

    }

    @Override
    public String getPublicLink(String bucket, String fileName) throws ServiceException {
        return null;
    }

    @Override
    public MdFileStoreInfo saveResourceFile(final MdFileStoreInfo mdFileStoreInfo, final InputStream in, final String productNo)
        throws ServiceException {
        FileOutputStream fos = null;
        final InputStream fin = null;
        String folder = productNo + DateUtil.date2StrByPattern(new Date(), DateUtil.DATE_FORMAT_STR);
        MdFileStoreInfo mdExist = this.mdFileStoreInfoMapper.selectByFolder(folder);
        while (mdExist != null) {
            // 如果该文件夹已存在,则更换,直到不存在为止
            folder = productNo + DateUtil.date2StrByPattern(new Date(), DateUtil.DATE_FORMAT_STR);
            mdExist = this.mdFileStoreInfoMapper.selectByFolder(folder);
        }
        mdFileStoreInfo.setFileFolder(folder);
        try {
            // 保存到本地
            final String tempDir = FileUtil.getTempDir();
            final String zipFileName = tempDir + mdFileStoreInfo.getFileOrgName();
            fos = new FileOutputStream(zipFileName);
            int index;
            final byte[] bytes = new byte[1024];
            while ((index = in.read(bytes)) != -1) {
                fos.write(bytes, 0, index);
            }
            fos.flush();
            // 解压zip文件到指定的文件夹
            final String destDir = this.absolutePath + File.separator + folder + File.separator;
            mdFileStoreInfo.setFileAddress(destDir);
            logger.info("zipFileName:" + zipFileName);
            logger.info("destDir:" + destDir);
            ZipUtil.unZip(zipFileName, destDir);
            // 检查格式
            final File unzipDir = new File(destDir);
            final String[] files = unzipDir.list();
            if (files.length == 0) {
                ZipUtil.deleteFiles(unzipDir);
                throw new ServiceException("压缩包内容为空");
            }
            int count = 0;
            for (final String fn : files) {
                if ("index.md".equalsIgnoreCase(fn)) {
                    count++;
                }
            }
            if (count == 0) {
                ZipUtil.deleteFiles(unzipDir);
                throw new ServiceException("压缩包内未包含index.md文件");
            } else if (count > 1) {
                ZipUtil.deleteFiles(unzipDir);
                throw new ServiceException("压缩包内包含多个index.md文件");
            }

           /*
           //该种方式使用jsch的ssh2  但是访问被拒绝  内网内两个服务所在的机器无法通过ssh命令进行跳转  行不通
           ScpConnectionEntity scpConnectionEntity = new ScpConnectionEntity();
            scpConnectionEntity.setIp("192.168.0.237");
            scpConnectionEntity.setPort(51888);
            //scpConnectionEntity.setPassword("");
            scpConnectionEntity.setUserName("root");
            fileSynchExecutorSupport.addTask(new FileSynchTask(unzipDir,scpConnectionEntity));
            */
            this.mdFileStoreInfoMapper.insert(mdFileStoreInfo);
            return mdFileStoreInfo;
        } catch (final ServiceException e) {
            throw e;
        } catch (final Exception e) {
            logger.error("文件上传失败,mdFileStoreInfo=", mdFileStoreInfo, e);
            throw new ServiceException("文件上传失败");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (final IOException e) {
                    logger.error("fos流关闭失败", e);
                }
            }
            if (fin != null) {
                try {
                    fin.close();
                } catch (final IOException e) {
                    logger.error("fin流关闭失败", e);
                }
            }
        }
    }

    @Override
    public String saveImageFile(final MultipartFile file, final String fileFolder) throws ServiceException {
        final String imageUrl = this.imageUrlPrefix + File.separator + fileFolder + File.separator;
        final String imageFilename = DateUtil.date2StrByPattern(new Date(), DateUtil.DATE_FORMAT_STR) + "."
            + FileUtil.cutSuffix(file.getOriginalFilename());
        final String imagePath = this.absolutePath + File.separator + fileFolder + File.separator + imageFilename;
        try {
            final File f = new File(imagePath);
            if (!f.getParentFile().exists()) {
                f.getParentFile().mkdir();
            }
            file.transferTo(f);
            return imageUrl + f.getName();
        } catch (final IOException e) {
            logger.error("图片上传失败,fileFolder=", fileFolder, e);
            throw new ServiceException("图片上传失败!");
        }
    }

    @Override
    public void deleteImageFile(String imgUrl) throws ServiceException {
        logger.info("要删除的图片路径:" + imgUrl);
        String resourcesFilePath ="";
        if(imgUrl.contains("/static/")){
            resourcesFilePath = imgUrl.replace("/static/",this.absolutePath + File.separator);
        }else if(imgUrl.startsWith("/")){
            resourcesFilePath = this.absolutePath + imgUrl;
        }else{
            resourcesFilePath = this.absolutePath + File.separator + imgUrl;
        }
        final File f = new File(resourcesFilePath);
        if(f.exists()){
            ZipUtil.deleteFiles(f);
        }
        logger.info("文件路径:" + resourcesFilePath + "的图片已删除");
    }

    @Override
    public void deleteBucket(String bucketName) throws ServiceException {

    }

}

2 minio存储

@Service
@ConditionalOnProperty(value = {"file.store.type"},havingValue = "minio")
public class MinioObjectStoreServiceImpl implements ObjectStoreService {
    private static final Logger logger = LoggerFactory.getLogger(MinioObjectStoreServiceImpl.class);

    @Value("${file.absolutepath}")
    private String absolutePath;

    @Value("${file.imageUrlPrefix}")
    private String imageUrlPrefix;

    private String url;
    private String accessKey;
    private String secretKey;

    @Resource
    private MdFileStoreInfoMapper mdFileStoreInfoMapper;

    Set<String> set = new HashSet<>();

    Map<String, String> map = new HashMap<>();
    {
        //otf, css, ico, svg, ttf, png, js, json, html, woff2, eot, woff
        map.put("otf","application/x-font-otf");
        map.put("css","text/css");
        map.put("ico","image/x-icon");
        map.put("svg","text/xml");
        map.put("ttf","application/octet-stream");
        map.put("png","image/png");
        map.put("jpe","image/jpeg");
        map.put("jpeg","image/jpeg");
        map.put("jpg","image/jpeg");
        map.put("js","application/x-javascript");
        map.put("html","text/html");
        map.put("woff2","application/x-font-woff");
        map.put("eot","application/octet-stream");
        map.put("woff","application/x-font-woff");
        map.put("zip","application/zip");
        map.put("md","text/x-markdown");
    }

    private MinioClient minioClient;

    public MinioObjectStoreServiceImpl(@Value("${minio.url}")String url,@Value("${minio.accesskey}")String accessKey,@Value("${minio.secretKey}")String secretKey ){
        //初始化minio
        this.url=url;
        this.accessKey=accessKey;
        this.secretKey=secretKey;
        init();
    }

    private void init(){
        logger.info("初始化Minio......");
        try {
            minioClient = MinioClient.builder().endpoint(url)
                    .credentials(accessKey, secretKey)
                    .build();
        } catch (Exception e) {
            logger.error("初始化Minio失败:", e);
            throw new RuntimeException("Minio客户端初始化失败");
        }
    }

    @Override
    public String uploadFile(String bucket, String fileName, InputStream in, Long size, String mime, Boolean autoMatch) throws ServiceException {
        try {
            if(StringUtil.isBlank(mime) && autoMatch){
                mime = map.get(FileUtil.cutSuffix(fileName));
            }
            logger.info("文件类型为:{}",mime);
            checkBucket(bucket,true);
            minioClient.putObject(PutObjectArgs.builder().bucket(bucket).contentType(mime).stream(in,size,PutObjectArgs.MIN_MULTIPART_SIZE).object(fileName).build() );
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("Minio关闭流异常,file=", fileName, e);
                }
            }
            return "/"+bucket+"/"+fileName;
        } catch (Exception e) {
            logger.error("上传文件失败,fileName={}", fileName, e);
            throw new ServiceException("上传文件失败");
        }
    }

    @Override
    public void downloadFile(String bucket, String remoteFileName, String localFileName) throws ServiceException {
        FileOutputStream fos = null;
        InputStream in = null;
        try {
            fos = new FileOutputStream(localFileName);
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(remoteFileName).build());
            int index;
            byte[] bytes = new byte[1024];
            //一个一个字节的读取并写入
            while((index = in.read(bytes)) != -1)
            {
                fos.write(bytes, 0, index);
            }
            fos.flush();
        } catch (Exception e) {
            logger.error("Minio下载文件失败,remoteFileName=", remoteFileName, e);
            throw new ServiceException("Minio下载文件失败");
        } finally {
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    logger.error("os流关闭失败", e);
                }
            }
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("in流关闭失败", e);
                }
            }
        }
    }

    @Override
    public String getDownloadLink(String bucket, String fileName, Integer expires) throws ServiceException{
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).expiry(expires).object(fileName).build() );
        } catch (Exception e) {
            logger.error("Minio获取文件下载链接失败,fileName=", fileName, e);
            throw new ServiceException("Minio获取文件下载链接失败");
        }
    }

    public String getPolicyStr(String bucketName){
        /*利用getBucketPolicy   在控制台设置创建一个bucket并设置访问策略以后获取到的
        {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::portalstaticbucket"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:GetObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::portalstaticbucket/*"]}]}*/
        StringBuilder builder = new StringBuilder();
        builder.append("{");
        builder.append("\"Version\":\"2012-10-17\",");
        builder.append("\"Statement\":[");
        builder.append("{");
        builder.append("\"Effect\":\"Allow\",");
        builder.append("\"Principal\":{\"AWS\":[\"*\"]},");
        builder.append("\"Action\":[");
        builder.append("\"s3:GetBucketLocation\",");
        builder.append("\"s3:ListBucket\",");
        builder.append("\"s3:ListBucketMultipartUploads\"");
        builder.append("],");
        builder.append("\"Resource\":[\"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"]},");
        builder.append("{");
        builder.append("\"Effect\":\"Allow\",");
        builder.append("\"Principal\":{\"AWS\":[\"*\"]},");
        builder.append("\"Action\":[");
        builder.append("\"s3:AbortMultipartUpload\",");
        builder.append("\"s3:DeleteObject\",");
        builder.append("\"s3:GetObject\",");
        builder.append("\"s3:ListMultipartUploadParts\",");
        builder.append("\"s3:PutObject\"");
        builder.append("],");
        builder.append("\"Resource\":[\"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"]");
        builder.append("}");
        builder.append("]");
        builder.append("}");
        //builder.append("{\"Version\": \"2012-10-17\", \"Statement\": [{ \"Effect\": \"Allow\", \"Action\": [ \"s3:*\"], \"Resource\": [ \"arn:aws:s3:::*\"] }] }");
        return builder.toString();
    }

    public boolean checkBucket(String bucketName,Boolean create) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
        logger.info("桶名称:{}",bucketName);
        boolean exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());

        if(!exist && create){
            //创建bucket
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            //设置访问策略
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(getPolicyStr(bucketName)).build());

            exist = true;
        }
        return exist;
    }

    public void batchUpload(String bucket, String path) throws ServiceException {
        try {
            checkBucket(bucket,true);
            File file = new File(path);
            System.out.println(file.getName());
            File[] files = file.listFiles();
            for(File item:files){
                uploadFile(bucket, item, "");
            }
            logger.info("Suffix set:", set);
        } catch (Exception e) {
            logger.error("Minio批量上传文件失败,path={}", path, e);
            throw new ServiceException("Minio批量上传文件失败");
        }
    }

    private void uploadFile(String bucket, File file, String parentPath) throws Exception{
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File item:files) {
                System.out.println(item.getName());
                uploadFile(bucket, item, parentPath+file.getName()+"/");
            }
        }else{
            InputStream in = new FileInputStream(file);
            logger.info("文件名称:{}",file.getName());
            logger.info("content-type名称:{}",map.get(FileUtil.cutSuffix(file.getName())));
            String contentType = map.get(FileUtil.cutSuffix(file.getName())) != null ? map.get(FileUtil.cutSuffix(file.getName())) : "application/octet-stream";
            minioClient.putObject(PutObjectArgs.builder().bucket(bucket).contentType(contentType).stream(in,file.length(),PutObjectArgs.MIN_MULTIPART_SIZE).object(parentPath+file.getName()).build() );
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    logger.error("Minio关闭流异常,file=", file.getName(), e);
                }
            }
        }
    }

    @Override
    public void batchDeleteFile(String bucket, String prefix) throws ServiceException{
        try {
            Iterable<Result<Item>> re = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).prefix(prefix).build() );
            Iterator<Result<Item>> reIterator = re.iterator();
            List<DeleteObject> list = new ArrayList<>();
            logger.info("批量删除开始");
            while(reIterator.hasNext()){
                String obname = reIterator.next().get().objectName();
                if(obname.endsWith("/") || obname.endsWith(".")){
                    batchDeleteFile(bucket,obname);
                }
                logger.info("对象名称;{}",obname);
                DeleteObject deleteObject = new DeleteObject(obname);
                list.add(deleteObject);
            }
            Iterable<Result<DeleteError>>  deleleResult = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucket).objects(list).build() );
            if(deleleResult != null){
                Iterator<Result<DeleteError>> deleleResultIterator = deleleResult.iterator();
                while(deleleResultIterator.hasNext()){
                    String obName = deleleResultIterator.next().get().objectName();
                    System.out.println(obName);
                    logger.error("Minio批量删除出现错误,obname={}", obName);
                }
            }
        } catch (Exception e) {
            logger.error("Minio批量删除失败:{}", bucket, prefix, e);
            throw new RuntimeException("Minio批量删除失败");
        }
    }

    @Override
    public String getPublicLink(String bucket, String fileName) throws ServiceException {
        return this.url+"/"+bucket+"/"+fileName;
    }

    @Override
    public MdFileStoreInfo saveResourceFile(MdFileStoreInfo mdFileStoreInfo, InputStream in, String productNo) throws ServiceException {
        FileOutputStream fos = null;
        final InputStream fin = null;
        String folder = productNo + DateUtil.date2StrByPattern(new Date(), DateUtil.DATE_FORMAT_STR);
        MdFileStoreInfo mdExist = this.mdFileStoreInfoMapper.selectByFolder(folder);
        while (mdExist != null) {
            // 如果该文件夹已存在,则更换,直到不存在为止
            folder = productNo + DateUtil.date2StrByPattern(new Date(), DateUtil.DATE_FORMAT_STR);
            mdExist = this.mdFileStoreInfoMapper.selectByFolder(folder);
        }
        mdFileStoreInfo.setFileFolder(folder);
        try {
            // 保存到本地
            final String tempDir = FileUtil.getTempDir();
            final String zipFileName = tempDir + mdFileStoreInfo.getFileOrgName();
            fos = new FileOutputStream(zipFileName);
            int index;
            final byte[] bytes = new byte[1024];
            while ((index = in.read(bytes)) != -1) {
                fos.write(bytes, 0, index);
            }
            fos.flush();
            // 解压zip文件到指定的文件夹
            final String destDir = this.absolutePath + File.separator + folder + File.separator;
            mdFileStoreInfo.setFileAddress(destDir);
            logger.info("zipFileName:" + zipFileName);
            logger.info("destDir:" + destDir);
            ZipUtil.unZip(zipFileName, destDir);
            // 检查格式
            final File unzipDir = new File(destDir);
            final String[] files = unzipDir.list();
            if (files.length == 0) {
                ZipUtil.deleteFiles(unzipDir);
                throw new ServiceException("压缩包内容为空");
            }
            int count = 0;
            for (final String fn : files) {
                if ("index.md".equalsIgnoreCase(fn)) {
                    count++;
                }
            }
            if (count == 0) {
                ZipUtil.deleteFiles(unzipDir);
                throw new ServiceException("压缩包内未包含index.md文件");
            } else if (count > 1) {
                ZipUtil.deleteFiles(unzipDir);
                throw new ServiceException("压缩包内包含多个index.md文件");
            }

           /*
           //该种方式使用jsch的ssh2  但是访问被拒绝  内网内两个服务所在的机器无法通过ssh命令进行跳转  行不通
           ScpConnectionEntity scpConnectionEntity = new ScpConnectionEntity();
            scpConnectionEntity.setIp("192.168.0.237");
            scpConnectionEntity.setPort(51888);
            //scpConnectionEntity.setPassword("");
            scpConnectionEntity.setUserName("root");
            fileSynchExecutorSupport.addTask(new FileSynchTask(unzipDir,scpConnectionEntity));
            */
            //minio批量上传
            batchUpload(folder,destDir);
            this.mdFileStoreInfoMapper.insert(mdFileStoreInfo);
            return mdFileStoreInfo;
        } catch (final ServiceException e) {
            throw e;
        } catch (final Exception e) {
            logger.error("文件上传失败,mdFileStoreInfo=", mdFileStoreInfo, e);
            throw new ServiceException("文件上传失败");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (final IOException e) {
                    logger.error("fos流关闭失败", e);
                }
            }
            if (fin != null) {
                try {
                    fin.close();
                } catch (final IOException e) {
                    logger.error("fin流关闭失败", e);
                }
            }
        }
    }

    @Override
    public String saveImageFile(MultipartFile file, String fileFolder) throws ServiceException {
        try {
            String fileName = file.getOriginalFilename();
            Long fileSize = file.getSize();
            logger.info("文件名称:{}",fileName);
            InputStream in = file.getInputStream();
            checkBucket(fileFolder,true);
            String url = uploadFile(fileFolder, fileName, in, fileSize, null, true);
            return url;
        } catch (Exception e) {
            logger.error("图片上传失败,fileFolder={}", fileFolder, e);
            throw new ServiceException("图片上传失败!");
        }

    }

    @Override
    public void deleteImageFile(String imgUrl) throws ServiceException {
        logger.info("要删除的图片路径:" + imgUrl);
        String objectName;
        String bucketName;
        if(imgUrl.startsWith("/")){
            objectName = imgUrl.substring(imgUrl.indexOf("/", 1) + 1);
            bucketName = imgUrl.substring(1,imgUrl.indexOf("/", 1));
        }else{
            objectName = imgUrl.substring(imgUrl.indexOf("/") + 1);
            bucketName = imgUrl.substring(0,imgUrl.indexOf("/"));
        }
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            logger.info("文件路径:" + imgUrl + "的图片已删除");
        } catch (Exception e) {
            logger.error("图片删除失败,imgUrl=", imgUrl, e);
            throw new ServiceException("图片删除失败!");
        }

    }

    @Override
    public void deleteBucket(String bucketName) throws ServiceException {
        try {
            boolean b = checkBucket(bucketName, false);
            if(b){
                batchDeleteFile(bucketName,null);
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            logger.error("bucket删除失败,bucketName={}", bucketName, e);
            throw new ServiceException("bucket删除失败!");
        }
    }


}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值