Minio的简单使用、利用Freemarker技术生成静态页面并传入MinIO

本文介绍了MinIO,一个开源的S3兼容对象存储服务,如何通过Docker部署、管理控制台操作、代码示例上传文件,以及将其集成到Spring Boot项目中实现页面静态化。重点展示了如何使用Golang构建的MinIO客户端和自定义工具类来管理对象存储。
摘要由CSDN通过智能技术生成

MinIo简介

MinIO是基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档。由于采用Golang实现,服务端可以工作在Windows,Linux, OS X和FreeBSD上。配置简单,基本是复制可执行程序,单行命令可以运行起来。

MinIO兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

S3 ( Simple Storage Service简单存储服务)

基本概念

  • bucket – 类比于文件系统的目录
  • Object – 类比文件系统的文件
  • Keys – 类比文件名

官网文档:http://docs.minio.org.cn/docs/

MinIo的使用

  1. 使用docker进行环境部署和启动
docker run -p 9000:9000 --name minio -d --restart=always -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data
  1. 管理控制台地址为: http://ip地址:端口

    按照环境部署时规定的账号密码登录,Access Key为minio,Secret_key 为minio123,进入系统后就可以看到主界面了

在这里插入图片描述

点击右下角的“+”号 ,点击下面的图标,创建一个桶

在这里插入图片描述

代码快速入门

  1. 导入依赖,此依赖是自定义的文件工具类模块,封装了一些操作Minio的方法
<dependency>
    <groupId>com.xiaobai</groupId>
    <artifactId>xiaobai-file-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  1. 创建测试类,将文件上传到minio服务器
@SpringBootTest(classes = MinIOApplication.class)
@RunWith(SpringRunner.class)
public class MinIOTest {

//    public static void main(String[] args) {
//
//        FileInputStream fileInputStream = null;
//        try {
//
//            fileInputStream =  new FileInputStream("D:\\list.html");;
//
//            //1.创建minio链接客户端
//            MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
//            //2.上传
//            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
//                    .object("list.html")//文件名
//                    .contentType("text/html")//文件类型
//                    .bucket("leadnews")//桶名词  与minio创建的名词一致
//                    .stream(fileInputStream, fileInputStream.available(), -1) //文件流
//                    .build();
//            minioClient.putObject(putObjectArgs);
//
//            System.out.println("http://192.168.200.130:9000/leadnews/ak47.jpg");
//
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
//    }

    @Autowired
    private FileStorageService fileStorageService;

    @Test
    public void testUpdateImgFile() {
        try {
            FileInputStream fileInputStream = new FileInputStream("D:\\pic1.png");
            String filePath = fileStorageService.uploadImgFile("", "mypic.jpg", fileInputStream);
            System.out.println(filePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

如果上传出现以下异常:

io.minio.errors.ErrorResponseException: The difference between the request time and the server's time is too large.

解决办法:在服务器上执行以下命令

ntpdate cn.pool.ntp.org

单独执行如上命令,只能临时解决时间同步,如果希望每次开启虚拟机都同步时间,需要将执行如下操作:

  1. 编辑 vim /etc/rc.local,将命令ntpdate cn.pool.ntp.org添加到最后一行
vim /etc/rc.local
  1. 给rc.local添加执行权限
chmod +x /etc/rc.d/rc.local
  1. 执行命令reboot重启系统,发现时间开机后能立即同步了

页面静态化集成minIO

实现方案

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t3Prz2ds-1653143830685)(C:\Users\白\AppData\Roaming\Typora\typora-user-images\1653067269673.png)]

实现步骤

  1. 在微服务中添加MinIO和freemarker的依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    <!--导入的此依赖是自定义的模块,封装了关于文件的工具类-->
    <dependency>
        <groupId>com.xiaobai</groupId>
        <artifactId>xiaobai-file-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
  1. 配置文件
  freemarker:
    cache: false  #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
    suffix: .ftl               #指定Freemarker模板文件的后缀名
    template-loader-path: classpath:/templates

minio:
  accessKey: minio
  secretKey: minio123
  bucket: leadnews #桶名称
  endpoint: http://192.168.200.130:9000 #minio的地址,表示文件写到哪里
  readPath: http://192.168.200.130:9000 #minio的地址,表示从哪里读文件
  1. 按照配置文件中模板的后缀名,在指定地址创建模板。举例:

在这里插入图片描述

  1. 编写代码
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest
@RunWith(SpringRunner.class)
public class ArticleHtmlTest {

    @Autowired
    private ApArticleContentService apArticleContentService;
    @Autowired
    private Configuration configuration;
    @Autowired
    private FileStorageService fileStorageservice; //自定义的工具类
    @Autowired
    private ApArticleService apArticleService;
    
    //利用Freemarker技术将数据按照模板生成文章详情的HTML页面并上传到MinIO中,然后更新文章表中的路径
    @Test
    public void testCreateHtmlAndSaveMinio() throws IOException, TemplateException {
        //通过文章id查询文章内表表中具体的文章内容对象
        ApArticleContent articleContent = apArticleContentService.getOne(Wrappers.<ApArticleContent>lambdaQuery()
                .eq(ApArticleContent::getArticleId, 1302862387124125698L));
        //获取具体的文章内容,转为JSON数组对象
        String content = articleContent.getContent();
        JSONArray array = JSONArray.parseArray(content);
        //为FTL模板准备数据:构建MAP,将数组封装到map中
        Map map = new HashMap<>();
        map.put("content", array);
        //使用Freemarker的Configuration获取模板,创建Template实例对象
        Template template = configuration.getTemplate("article.ftl");
        //创建输出流,利用模块对象调用process方法,将数据按照模板转换成静态页面放到输出流中
        StringWriter out = new StringWriter();
        template.process(map, out);
        //将输出流转成输入流
        ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toString().getBytes());
        //利用Minio工具类,将输入流的数据上传到Minio中,并获取访问地址
        String url = fileStorageservice.uploadHtmlFile("", articleContent.getId().toString(), inputStream);
        //将静态url地址更新保存到article表中
        ApArticle aparticle = new ApArticle();
        aparticle.setId(articleContent.getArticleId());
        aparticle.setStaticUrl(url);
        apArticleService.updateById(aparticle);
    }
}

自定义的Minio工具类

接口:

import java.io.InputStream;

/**
 * @author xiaobai
 */
public interface FileStorageService {

    /**
     *  上传图片文件
     * @param prefix  文件前缀
     * @param filename  文件名
     * @param inputStream 文件流
     * @return  文件全路径
     */
    public String uploadImgFile(String prefix, String filename,InputStream inputStream);

    /**
     *  上传html文件
     * @param prefix  文件前缀
     * @param filename   文件名
     * @param inputStream  文件流
     * @return  文件全路径
     */
    public String uploadHtmlFile(String prefix, String filename,InputStream inputStream);

    /**
     * 删除文件
     * @param pathUrl  文件全路径
     */
    public void delete(String pathUrl);

    /**
     * 下载文件
     * @param pathUrl  文件全路径
     * @return
     *
     */
    public byte[]  downLoadFile(String pathUrl);
}

工具类的实现类:

import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@EnableConfigurationProperties(MinIOConfigProperties.class)
@Import(MinIOConfig.class)
public class MinIOFileStorageService implements FileStorageService {

    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    private final static String separator = "/";

    /**
     * @param dirPath
     * @param filename  yyyy/mm/dd/file.jpg
     * @return
     */
    public String builderFilePath(String dirPath,String filename) {
        StringBuilder stringBuilder = new StringBuilder(50);
        if(!StringUtils.isEmpty(dirPath)){
            stringBuilder.append(dirPath).append(separator);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String todayStr = sdf.format(new Date());
        stringBuilder.append(todayStr).append(separator);
        stringBuilder.append(filename);
        return stringBuilder.toString();
    }

    /**
     *  上传图片文件
     * @param prefix  文件前缀
     * @param filename  文件名
     * @param inputStream 文件流
     * @return  文件全路径
     */
    @Override
    public String uploadImgFile(String prefix, String filename,InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("image/jpg")
                    .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1)
                    .build();
            minioClient.putObject(putObjectArgs);
            StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
            urlPath.append(separator+minIOConfigProperties.getBucket());
            urlPath.append(separator);
            urlPath.append(filePath);
            return urlPath.toString();
        }catch (Exception ex){
            log.error("minio put file error.",ex);
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     *  上传html文件
     * @param prefix  文件前缀,可以不填
     * @param filename   文件名
     * @param inputStream  文件流
     * @return  文件全路径
     */
    @Override
    public String uploadHtmlFile(String prefix, String filename,InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("text/html")
                    .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1)
                    .build();
            minioClient.putObject(putObjectArgs);
            StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
            urlPath.append(separator+minIOConfigProperties.getBucket());
            urlPath.append(separator);
            urlPath.append(filePath);
            return urlPath.toString();
        }catch (Exception ex){
            log.error("minio put file error.",ex);
            ex.printStackTrace();
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 删除文件
     * @param pathUrl  文件全路径
     */
    @Override
    public void delete(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/","");
        int index = key.indexOf(separator);
        String bucket = key.substring(0,index);
        String filePath = key.substring(index+1);
        // 删除Objects
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}",pathUrl);
            e.printStackTrace();
        }
    }

    /**
     * 下载文件
     * @param pathUrl  文件全路径
     * @return  文件流
     */
    @Override
    public byte[] downLoadFile(String pathUrl)  {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/","");
        int index = key.indexOf(separator);
        String bucket = key.substring(0,index);
        String filePath = key.substring(index+1);
        InputStream inputStream = null;
        try {
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}",pathUrl);
            e.printStackTrace();
        }
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while (true) {
            try {
                if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
            } catch (IOException e) {
                e.printStackTrace();
            }
            byteArrayOutputStream.write(buff, 0, rc);
        }
        return byteArrayOutputStream.toByteArray();
    }
}
import com.xiaobai.file.service.FileStorageService;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@EnableConfigurationProperties({MinIOConfigProperties.class})
//当引入FileStorageService接口时,就会加载此配置类
@ConditionalOnClass(FileStorageService.class)
public class MinIOConfig {

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    @Bean
    public MinioClient buildMinioClient() {
        return MinioClient
                .builder()
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                .endpoint(minIOConfigProperties.getEndpoint())
                .build();
    }
}
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.io.Serializable;

@Data
@ConfigurationProperties(prefix = "minio")  // 文件上传 配置前缀file.oss
public class MinIOConfigProperties implements Serializable {
    private String accessKey;
    private String secretKey;
    private String bucket;
    private String endpoint;
    private String readPath;
}

**

注意

**:此时的自定义模块没有启动类,是无法生效的。利用springboot自动装配原理,在resource模块下创建META-INF包,在包中创建spring.factories文件,将自定义的类写入到这里,就可以让spring加载到了

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.xiaobai.file.service.impl.MinIOFileStorageService
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值