阅读 268

SpringBoot自定义Starter 并制作一个简单的图床

上篇博客中讲述了从SpringBoot源码到自己封装一个Starter,而并没有写一个真正的业务场景,这篇博客将自定义starter 添加第三方组件(图片存储OSS);并根据自定义的starter制作一个图床;

项目结构:

oss-spring-boot-project
│   README.md
│   pom.xml   
└───oss-spring-boot-parent
│		 │  pom.xml
│───oss-spring-boot-autoconfigure
│        │  src
│        │  pom.xml  
└───oss-spring-boot-starter
          │ pom.xml
复制代码

这里放上源码地址看的更明了些!github.com/haoxiaoyong…

下层依赖上层;其中主要逻辑都在oss-spring-boot-autoconfigure模块中;

开发步骤

创建整体结构
  • 创建一个名字为oss-spring-boot-project的工程

    • 这个pom.xml中主要存放一些上传到maven中央仓库所需的一些build插件以及module的管理
  • 首先创建一个名字为oss-spring-boot-parent的module

    • 这个工程主要是对项目依赖版本的管理,避免一些冲突;
  • 然后创建一个名字为oss-spring-boot-autoconfigure的module

    • 这里是自动装配的主要代码逻辑
  • 之后创建一个名字为oss-spring-boot-starter的module

    • 这是主要提供给使用者使用的maven dependency
代码逻辑实现

oss-spring-boot-autoconfigure模块下的pom.xml文件中添加依赖:

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.8.0</version>
        </dependency>
    </dependencies>
复制代码

创建自动配置类

@Configuration
@EnableConfigurationProperties(OssProperties.class)
@ConditionalOnClass(OssLocalBean.class)
@ConditionalOnWebApplication
public class OssStarterAutoConfiguration {


    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "oss.config", name = "enable", havingValue = "true")
    public OssLocalBean defaultOssLocalBean(OssProperties ossProperties) {
        OssLocalBean oss = new OssLocalBean();
        oss.setAccessKeyId(ossProperties.getAccessKeyId());
        oss.setAccessKeySecret(ossProperties.getAccessKeySecret());
        oss.setBucketName(ossProperties.getBucketName());
        oss.setEndpoint(ossProperties.getEndpoint());
        oss.setEnable(ossProperties.getEnable());
        return oss;
    }

    @Bean
    @ConditionalOnMissingBean
    public OssService ossService () {
        return new OssService();
    }
}

复制代码

每个注解的具体作用已经在上一篇博客从SpringBoot源码到自己封装一个Starter说明的很清楚了,这里就不过多的叙述了;

新建一个OssProperties,声明该starter的使用者可以配置哪些配置项。

@ConfigurationProperties(prefix = "oss.config")
public class OssProperties {

   private String endpoint;

   private String accessKeyId;

   private String accessKeySecret;

   private String bucketName;

   private Boolean enable;
    //....set
    //....get
}
复制代码

使用过oss的都同学应该都知道除了enable是我自定义的以外其他都是必填项了;

resources目录下新建一个META-INF目录并且创建一个spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.haoxiaoyong.oss.starter.config.OssStarterAutoConfiguration
复制代码

下面就是需要我们自己去封装使用oss的功能了,例如上传文件,下载文件,删除文件等等。。。

新建一个OssService功能类:

public class OssService {

    @Autowired
    private OssLocalBean ossLocalBean;

    /**
     *	文件形式上传
     * @param key
     * @param file
     * @return
     */
    public String upload(String key, File file) {

        if (ossLocalBean.getEnable()) {

            OSS ossClient = null;
            try {
                ossClient = getClient();
                ossClient.putObject(ossLocalBean.getBucketName(), key, file);

                Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
                URL url = ossClient.generatePresignedUrl(ossLocalBean.getBucketName(), key, expiration);
                return ossLocalBean.getProtocol() + url.getHost() + url.getPath();
            } finally {
                ossClient.shutdown();
            }
        }
        return null;
    }

    /**
     * 流式上传
     * @param key
     * @param inputStream
     * @return
     */
    public String upload(String key, InputStream inputStream) {
        if (ossLocalBean.getEnable()) {
            OSS ossClient = null;
            try {
                ossClient = getClient();
                ossClient.putObject(ossLocalBean.getBucketName(), key, inputStream);
                Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
                URL url = ossClient.generatePresignedUrl(ossLocalBean.getBucketName(), key, expiration);
                return ossLocalBean.getProtocol() + url.getHost() + url.getPath();

            } finally {
                ossClient.shutdown();

            }
        }
        return null;
    }

    /**
     * 下载文件
     * @param key
     * @return
     * @throws IOException
     */
    public BufferedReader download(String key) throws IOException {
        if (ossLocalBean.getEnable()) {
            OSS ossClient = null;
            BufferedReader reader = null;
            try {
                ossClient = getClient();
                OSSObject ossObject = ossClient.getObject(ossLocalBean.getBucketName(), key);
                reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
                while (true) {
                    String line = reader.readLine();
                    if (line == null) {
                        break;
                    }
                }
                reader.close();
                return reader;
            } finally {
                ossClient.shutdown();

            }
        }
        return null;
    }

    /**
     * 文件是否存在
     * @param key
     */
   
    public boolean exist(String key) {
        if (ossLocalBean.getEnable()) {

            OSS ossClient = null;

            try {
                ossClient = getClient();
                return ossClient.doesObjectExist(ossLocalBean.getBucketName(), key);
            } finally {
                ossClient.shutdown();

            }
        }
        return false;
    }

    /**
     * 删除文件
     * @param key
     */
    public void delete(String key) {
        if (ossLocalBean.getEnable()) {
            OSS ossClient = null;

            try {
                ossClient = getClient();
                ossClient.deleteObject(ossLocalBean.getBucketName(), key);
            } finally {
                ossClient.shutdown();

            }
        }
    }

    /**
     * get oss Client
     *
     * @return
     */
    private OSS getClient() {
        return new OSSClientBuilder().build(ossLocalBean.getEndpoint(), ossLocalBean.getAccessKeyId(), ossLocalBean.getAccessKeySecret());
    }
复制代码

到这里基本上和上一篇博客从SpringBoot源码到自己封装一个Starter流程是一致的!

接下来就是将oss-spring-boot-autoconfigure模块的groupId,artifactId,version引入到oss-spring-boot-starter模块中!

至此整个starter工程就完成了,接下来将其上传到maven中央仓库;这样任何人想使用都直接将

<dependency>
   <groupId>cn.haoxiaoyong.oss</groupId>
   <artifactId>oss-spring-boot-starter</artifactId>
   <version>0.0.2-beta</version>
</dependency>
复制代码

此maven依赖添加pom.xml文件中即可!下图该项目在maven中央仓库的展示:

该工程代码:SpringBoot自定义starter 添加第三方组件;欢迎star!

使用自定义Starter制作一个图床

先看一下效果:

创建oss-picture-manage工程;依赖maven依赖

 <dependencies>
    <dependency>
        <groupId>cn.haoxiaoyong.oss</groupId>
        <artifactId>oss-spring-boot-starter</artifactId>
        <version>0.0.2-beta</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.1.8.RELEASE</version>
    </dependency>
 </dependencies>
复制代码

application.yml中配置:

oss:
  config:
    enable: true
    endpoint: 
    access-key-id: 
    access-key-secret: 
    bucket-name: 
    dir: blog/
复制代码

前端代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>pic manage</title>
    <link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css">
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>
    <script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script>
</head>
<body>
<div id="test">

    <el-upload
            class="upload-demo"
            drag
            action="http://work.haoxiaoyong.cn:8876/upload"
            multiple="true"
            align="center"
            :on-success="handleSuccess"
    >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text" style="font-size: 20px">将文件拖到此处,或<em>点击上传</em></div>
        <!--<div class="el-upload__tip" slot="tip">,且不超过500kb</div>-->
    </el-upload>
</div>

<footer align="center">
    <p>&copy; OSS 图床</p>
</footer>
<style>
    .el-upload-dragger {
        width: 688px;
        height: 300px;
    }
</style>

<script>
    var vue = new Vue({
        el: "#test",
        data: {},
        methods: {
            handleSuccess: function (response) {
                if (response.toString() === "fail") {
                    this.$message.error('上传失败');
                    return;
                }
                var h = this.$createElement;
                this.$notify({
                    title: '上传成功',
                    message: h('p', {style: 'color: teal'}, [
                        h('a', {style: 'word-break: break-word'}, response)
                    ]),
                    type: 'success',
                    duration: 0
                });
            }

        }
    });

</script>
</body>
</html>
复制代码

Controller以及Service:

@RestController
public class UploadController {


    @Autowired
    private UploadService uploadService;

    @RequestMapping("upload")
    public String upload(MultipartFile file) {
        return uploadService.upload(file);
    }
}

@Service
public class UploadService {

    @Autowired
    private OssService ossService;

    @Value("${oss.config.dir}")
    private String fileDir;

    public String upload(MultipartFile file) {
        String url = "fail";
        String fileName = fileDir + file.getOriginalFilename();
        try {
            if (!ossService.exist(fileName)) {
                url = ossService.upload(fileName, file.getInputStream());
                if (!StringUtils.isEmpty(url)) {
                    return url;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return url;
    }
}
复制代码

至此,制作并使用Springboot Starter就完成了!可以直接使用此项目作为博客图床,此项目代码地址:github.com/haoxiaoyong… 欢迎star!