SpringBoot2.x整合(cos,oss,本地挂载盘)文件存储的小而美的项目实现

由于项目升级,文件存储由本地磁盘(挂载盘)升级为cos(腾讯云)存储,由此实现了一套整合各家文件存储的项目。

1. 设计思想

1.1 使用到的设计模式

  1. 例如cos和oss提供的client不同,以及本地磁盘存储需要自己实现client端。所以需要自己实现一个公共的接口,使用适配器模式,将各种client端转换为统一的client接口实现;
  2. 业务端依旧使用Template类操作文件,Template类使用模板方法模式,来支持业务放进行扩展。
  3. Template类通过组合的方式选择client类,使用策略模式来自由的切换存储介质(cos,oss,本地磁盘)。

1.2 设计思想

项目默认提供cos腾讯云存储和本地挂载盘存储的template类。当业务方在依赖中移除腾讯cos的依赖,且使用阿里云oss的依赖后。template类将自动切换为阿里云oss的存储。

        <dependency>
            <groupId>com.tellme</groupId>
            <artifactId>xos-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <artifactId>cos_api</artifactId>
                    <groupId>com.qcloud</groupId>
                </exclusion>
                <exclusion>
                    <groupId>com.tencent.cloud</groupId>
                    <artifactId>cos-sts-java</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
        </dependency>

其思想借鉴与SpringBoot2.x-Redis实现Jedis客户端和Lettuce客户端的切换。

2. 代码实现

2.1 pom依赖

pom依赖中,使用SpringBoot2.0.4版本。

引入了

  1. 腾讯云cos提供的jar;
  2. 阿里云oss提供的jar;
  3. 以及相关的工具jar;

当然阿里云oss的依赖不向下传递。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tellme</groupId>
    <artifactId>xos-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- 通信-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.8.1</version>
        </dependency>


        <dependency>
            <groupId>com.qcloud</groupId>
            <artifactId>cos_api</artifactId>
            <version>5.6.8</version>
        </dependency>
        <dependency>
            <groupId>com.tencent.cloud</groupId>
            <artifactId>cos-sts-java</artifactId>
            <version>3.0.5</version>
        </dependency>
        <!--jar不向下传递(阿里云oos)-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
            <scope>provided</scope>
        </dependency>


    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>3.0.1</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.2 starter的实现

在spring.factories文件中定义自动装配的类。

image.png
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tellme.os.config.OSAutoConfiguration

2.3 client端(适配器模式)

首先是需要提供一个项目内公共的接口类(OSClient)。无论各个厂商的client客户端都要进行适配。

public interface OSClient {

    /**
     * 上传文件
     *
     * @param storageName  (bucketName)存储桶
     * @param relativePath 文件上传的地址
     * @param fileBytes    文件流
     */
    void uploadFile(String storageName, String relativePath, byte[] fileBytes);

    /**
     * 下载文件
     *
     * @param filePath 文件地址
     * @return 文件输出流
     */
    ByteArrayOutputStream downloadFile(String filePath);

    /**
     * 客户端关闭
     */
    void shutdown();
}

由于云文件存储,下载文件的方法是相同的,故可以使用模板方法模式来进行扩展。

@Slf4j
public abstract class NetOsClient implements OSClient {

    @Override
    public ByteArrayOutputStream downloadFile(String filePath) {
        ByteArrayOutputStream data;
        data = OkHttpUtil.get(filePath);
        if (data == null) {
            throw new OsException("下载照片失败,照片地址:" + filePath);
        }
        //转换流信息
        return data;
    }

}

2.3.1 适配腾讯云cos客户端

@Data
public class CosClient extends NetOsClient {

    private COSClient cosClient;

    /**
     *
     * @param bucketName   存储桶
     * @param relativePath 文件上传的地址
     * @param fileBytes    文件流
     */
    @Override
    public void uploadFile(String bucketName, String relativePath, byte[] fileBytes) {
        ByteArrayInputStream input = new ByteArrayInputStream(fileBytes);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        // 设置输入流长度
        objectMetadata.setContentLength(input.available());
        cosClient.putObject(bucketName, relativePath, input, objectMetadata);
    }
    /**
     * 关闭客户端
     */
    @Override
    public void shutdown() {
        cosClient.shutdown();
    }
}

2.3.2 适配阿里云oss客户端

@Data
public class OssClient extends NetOsClient{

    private OSSClient ossClient;

    @Override
    public void uploadFile(String bucketName, String relativePath, byte[] fileBytes) {
        ByteArrayInputStream input = new ByteArrayInputStream(fileBytes);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        // 设置输入流长度
        objectMetadata.setContentLength(input.available());
        ossClient.putObject(bucketName, relativePath, input, objectMetadata);
    }

    @Override
    public void shutdown() {
        ossClient.shutdown();
    }
}

2.3.3 适配本地挂载盘客户端

@Slf4j
public class FileOsClient implements OSClient {

    /**
     * @param bucketName  上传的根路径
     * @param relativePath 文件上传的地址
     * @param fileBytes    文件流
     */
    @Override
    public void uploadFile(String bucketName, String relativePath, byte[] fileBytes) {
        File file = new File(bucketName + File.separator + relativePath);
        //获取文件夹目录
        String fullPath = file.getParent();
        File dir = new File(fullPath);
        //创建目录
        dir.mkdirs();
        try {
            //构建输出流
            ByteArrayInputStream is = new ByteArrayInputStream(fileBytes);
            FileOutputStream os = new FileOutputStream(file);
            byte[] b = new byte[1024];
            int nRead;
            while ((nRead = is.read(b)) != -1) {
                os.write(b, 0, nRead);
            }
        } catch (Exception e) {
            log.error("", e);
            throw new OsException("", e);
        }
    }

    /**
     * 读取本地文件
     *
     * @param filePath 文件地址
     * @return 文件数组输出流
     */
    @Override
    public ByteArrayOutputStream downloadFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new OsException("文件不存在!");
        }
        InputStream is = IOUtil.getInputStream(file);
        return IOUtil.convertInputStream(is);
    }

    @Override
    public void shutdown() {

    }

}

2.4 template类(模板模式+策略模式)

  1. template通过组合的方式引入client对象,在Spring容器初始化的时候选择合适的client策略。
  2. template采用模板方法模式,可自由扩展。
public interface OsTemplate {

    /**
     * 上传文件
     *
     * @param relativePath 上传文件的相对地址
     * @param fileBytes    文件流
     * @return 上传后的地址
     */
    String saveFile(String relativePath, byte[] fileBytes);

    /**
     * 拼接路径,上传照片
     *
     * @param modulePath 项目路径
     * @param fileSuffix 文件后缀
     * @param base64Str  base64
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffixEnum.getType()}
     */
    String saveFile(String modulePath, String fileSuffix, String base64Str);

    /**
     * 拼接路径,上传文件
     *
     * @param modulePath 项目路径
     * @param fileBytes  文件流
     * @param fileSuffix 文件后缀
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffixEnum.getType()}
     */
    String saveFile(String modulePath, byte[] fileBytes, String fileSuffix);


    /**
     * 拼接路径,上传到特定的存储桶
     *
     * @param bucketName 存储桶的名字
     * @param modulePath 项目路径
     * @param fileSuffix 文件后缀
     * @param fileBytes  文件流
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffix}
     */
    String saveFile(String bucketName, String modulePath, String fileSuffix, byte[] fileBytes);

    /**
     * 拼接路径,上传到特定的存储桶
     *
     * @param bucketName   存储桶的名字
     * @param relativePath 文件的相对路径
     * @param fileBytes    文件流
     * @return 格式:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffix}
     */
    String saveFile(String bucketName, String relativePath, byte[] fileBytes);


    /**
     * 文件下载
     *
     * @param filePath 文件地址
     * @return 下载字节流
     */
    ByteArrayOutputStream downloadFile(String filePath);

    /**
     * CDN前缀地址
     *
     * @return CDN地址
     */
    String getCdnHost();
}

2.4.1 云存储的template

@Data
@Slf4j
public class NetOsTemplate implements OsTemplate {

    //cos连接对象
    protected OSClient client;

    protected OsConfigProperties osConfigProperties;

    /**
     * 存储介质:
     * cos存储的是:bucketName
     * file存储的是:根路径
     */
    protected String bucketName;

    /**
     * cdn地址
     */
    protected String cdnHost;

    protected static  String PATH_COMMON = "/xos";


    /**
     * (经常使用)使用配置文件的存储桶上传照片
     *
     * @param modulePath 模块路径
     * @param fileSuffix 文件后缀,需要携带.
     * @param base64Str  照片的base64信息
     * @return 图片上传的地址
     */
    public String saveFile(String modulePath, String fileSuffix, String base64Str) {
        return saveFile(bucketName, modulePath, fileSuffix, IOUtil.base64ToBytes(base64Str));
    }


    /**
     * (经常使用)使用配置文件的存储桶上传文件
     *
     * @param modulePath 模块路径
     * @param fileSuffix 文件后缀,需要携带.
     * @param fileBytes  文件流字节数组
     * @return 图片上传的地址
     */
    public String saveFile(String modulePath, byte[] fileBytes, String fileSuffix) {
        return saveFile(bucketName, modulePath, fileSuffix, fileBytes);
    }

    /**
     * 腾讯云上传文件
     * 只需要传入模块目录,自动拼装上传腾讯云的路径。
     * {@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffixEnum.getType()}
     *
     * @param bucketName 存储目录
     * @param modulePath  模块路径(业务路径格式:{@code exercise/plan/answerImage })
     * @param fileSuffix  文件后缀,需要携带.
     * @param fileBytes   文件流字节数组
     * @return 图片上传的地址
     */
    public String saveFile(String bucketName, String modulePath, String fileSuffix, byte[] fileBytes) {
        return saveFile(bucketName, getRelativeDir(modulePath) + getFileName(fileSuffix), fileBytes);
    }

    /**
     * 腾讯云上传文件
     * 返回的路径:{@code PATH_COMMON / modulePath / calculatePath() /UUID.randomUUID() + fileSuffix}
     *
     * @param relativePath 上传文件的相对地址
     * @param fileBytes    文件流
     * @return 保存到腾讯云的地址
     */
    @Override
    public String saveFile(String relativePath, byte[] fileBytes) {
        //读取配置信息的存储桶信息
        return saveFile(bucketName, relativePath, fileBytes);
    }

    /**
     * 腾讯云上传文件
     *
     * @param bucketName  存储桶
     * @param relativePath 文件的相对地址
     * @param fileBytes    文件流的字节数组
     * @return 腾讯云上的相对地址
     */
    public String saveFile(String bucketName, String relativePath, byte[] fileBytes) {
        client.uploadFile(bucketName, relativePath, fileBytes);
        return relativePath;
    }


    /**
     * 文件下载
     * 若下载失败或者照片不存在,抛出{@link com.xdf.pscommon.os.exception.OsException}异常。
     *
     * @param filePath 文件地址
     * @return 文件输出流
     */
    @Override
    public ByteArrayOutputStream downloadFile(String filePath) {
        return client.downloadFile(filePath);
    }


    /**
     * 获取腾讯云的前缀地址
     *
     * @return 腾讯云的CDN地址
     */
    public String getCdnHost() {
        return cdnHost;
    }

    /**
     * 创建文件夹的相对路径
     *
     * @return 格式:{@code exam/modulePath/2021/11/1}
     */
    protected String getRelativeDir(String modulePath) {
        return PATH_COMMON + File.separator + modulePath + File.separator + calculatePath();
    }

    /**
     * 创建文件名(使用UUID创建)
     */
    protected String getFileName(String fileSuffix) {
        return UUID.randomUUID() + fileSuffix;
    }

    /**
     * 时间戳作为路径名
     */
    protected String calculatePath() {
        Calendar calendar = Calendar.getInstance();
        String year = String.valueOf(calendar.get(Calendar.YEAR));
        String month = String.valueOf(calendar.get(Calendar.MONTH) + 1);
        String date = String.valueOf(calendar.get(Calendar.DATE));
        return year + "/" + month + "/" + date + "/";
    }

}

2.4.2 挂载盘存储的template

挂载盘在创建目录时,需要随机路由到不同的挂载盘上,所以需要重写getRelativeDir方法即可。

public class FileOsTemplate extends NetOsTemplate {

    private long index;

    private final static String ROOTPATH = "rootPath";
    private final static String FORWARDMARK = "forwardMark";


    /**
     * 路径填充挂载盘路径
     */
    @Override
    public String getRelativeDir(String modulePath) {
        OperatingSystemEnum operatingSystem = IOUtil.getOperatingSystem();
        if (OperatingSystemEnum.LINUX.equals(operatingSystem) &&
                osConfigProperties.getFile().getRootPath() != null &&
                osConfigProperties.getFile().getRootPath().size() > 1) {
            Map<String, String> rootPathMap = returnRootPathMap(osConfigProperties.getFile().getRootPath());
            return PATH_COMMON + File.separator + rootPathMap.get(FORWARDMARK) + File.separator + modulePath + File.separator + calculatePath();
        } else {
            return super.getRelativeDir(modulePath);
        }
    }

    /**
     * 返回挂载盘的具体配置
     */
    private Map<String, String> returnRootPathMap(Map<String, String> rootPaths) {
        int size = 2; //最少有个原磁盘以及加的共享盘,共享磁盘个数
        if (rootPaths != null && rootPaths.size() > 1) {
            size = rootPaths.size() - 1;
        }
        int rootPathIndex = Math.toIntExact(index++ % size);
        Map<String, String> rootPathMap = new HashMap<>();
        switch (rootPathIndex) {
            case 0:
                rootPathMap.put(ROOTPATH, rootPaths.get("files1"));
                rootPathMap.put(FORWARDMARK, "files1/");
                break;
            case 1:
                rootPathMap.put(ROOTPATH, rootPaths.get("files2"));
                rootPathMap.put(FORWARDMARK, "files2/");
                break;
            case 2:
                rootPathMap.put(ROOTPATH, rootPaths.get("files3"));
                rootPathMap.put(FORWARDMARK, "files3/");
                break;
            default:
                rootPathMap.put(ROOTPATH, rootPaths.get("files"));
                rootPathMap.put(FORWARDMARK, "files/");
                break;
        }
        return rootPathMap;
    }

}

2.5 加入到Spring容器中

  1. 使用@ConditionalOnClass注解,当某个依赖存在时,才加载bean类;
  2. 使用@ConditionalOnProperty注解,当某个配置存在时,才加载bean类;
  3. 使用@Import注解,当多个配置均存在时,优先加载哪个配置;
  4. 配合@ConditionalOnMissingBean(name = "netOsTemplate")注解,当加载某个配置后,便不加载同名的bean。

2.5.1 配置类

当本地磁盘加载时,支持windows,mac,linux各个配置。

@Data
@ConfigurationProperties("xos")
public class OsConfigProperties {

    /**
     * 腾讯云COS配置
     */
    private CosProperties cos;

    /**
     * 阿里云OOS配置
     */
    private OssProperties oss;
    /**
     * 共享盘文件存储配置
     */
    private FileProperties file;

    /**
     * 云存储——存储桶
     */
    private String bucketName;
    /**
     * 云存储——cdn地址
     */
    private String cdnHost;

    @Data
    public static class CosProperties {

        private String accessKeyId;

        private String accessKeySecret;

        private String region;


    }


    @Data
    public static class OssProperties {
        private String endpoint;

        private String accessKeyId;

        private String accessKeySecret;

    }


    @Data
    public static class FileProperties {
        /**
         * Linux支持的根路径
         */
        private String linuxRootPath;
        /**
         * window支持的根路径
         */
        private String windowsRootPath;
        /**
         * mac支持的根路径
         */
        private String macRootPath;

        /**
         * Linux系统设置,挂载盘映射关系
         */
        private Map<String, String> rootPath;
        /**
         * cdn地址
         */
        private String cdnHost;
    }
}

2.5.2 腾讯云cos的client加入到Spring中

只有存在COSClient.class类且存在xos.cos.accessKeyId配置时,才会加载改bean。

@Configuration
@ConditionalOnClass(COSClient.class)
public class CosClientConfiguration {

    /**
     * 初始化腾讯云客户端
     */
    @Bean(name = "netOSClient", destroyMethod = "shutdown")
    @ConditionalOnProperty("xos.cos.accessKeyId")
    @ConditionalOnMissingBean(name = "netOSClient")
    public OSClient cosClient(OsConfigProperties osConfigProperties) {
        CosClient cosClient = new CosClient();
        OsConfigProperties.CosProperties cosProperties = osConfigProperties.getCos();
        // 1 初始化用户身份信息(secretId, secretKey)。
        COSCredentials cred = new BasicCOSCredentials(cosProperties.getAccessKeyId(), cosProperties.getAccessKeySecret());
        // 2 设置 bucket 的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
        Region cosRegion = new Region(cosProperties.getRegion());
        ClientConfig clientConfig = new ClientConfig(cosRegion);
        // 3 生成 cos 客户端。
        COSClient cosClient = new COSClient(cred, clientConfig);
        cosClient.setCosClient(cosClient);
        return cosClient;
    }

}

2.5.3 阿里云oss的client加入到Spring容器

@Configuration
@ConditionalOnClass(OSSClient.class)
public class OssClientConfiguration {

    /**
     * 初始化阿里云oss客户端
     */
    @Bean(name = "netOSClient", destroyMethod = "shutdown")
    @ConditionalOnProperty("xos.oss.accessKeyId")
    @ConditionalOnMissingBean(name = "netOSClient")
    public  OSClient ossClient(OsConfigProperties osConfigProperties) {
        OssClient ossClient = new OssClient();
        OsConfigProperties.OssProperties ossProperties = osConfigProperties.getOss();
        OSSClient ossClient = new OSSClient(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
        ossClient.setOssClient(ossClient);
        return ossClient;
    }
}

2.5.4 自动装配的config

  1. @EnableConfigurationProperties将配置文件类加入到Spring容器;
  2. @Import({CosClientConfiguration.class,OssClientConfiguration.class})将config类加入到Spring容器;
@Slf4j
@Configuration
@EnableConfigurationProperties(value = OsConfigProperties.class)
@Import({CosClientConfiguration.class,OssClientConfiguration.class})
public class OSAutoConfiguration {



    /**
     * 初始化文件流客户端
     */
    @Bean
    @ConditionalOnMissingBean(name = "fileClient")
    public lFileOsClient fileClient(OsConfigProperties osConfigProperties) {
        return new FileOsClient();
    }


    /**
     * 创建云存储的模板类
     *
     * @param oSClient           cos的客户端
     * @param osConfigProperties 配置信息
     * @return 模板工具类
     */
    @Bean
    @Primary
    @ConditionalOnBean(name = {"netOSClient"})
    @ConditionalOnMissingBean(name = "netOsTemplate")
    public NetOsTemplate netOsTemplate(@Qualifier("netOSClient") OSClient oSClient, OsConfigProperties osConfigProperties) {
        NetOsTemplate osTemplate = new NetOsTemplate();
        //放入客户端
        osTemplate.setClient(oSClient);
        osTemplate.setBucketName(osConfigProperties.getBucketName());
        //配置文件
        osTemplate.setOsConfigProperties(osConfigProperties);
        osTemplate.setCdnHost(osConfigProperties.getCdnHost());
        return osTemplate;
    }

    /**
     * 共享盘OS的模板类
     */
    @Bean
    @ConditionalOnMissingBean(name = "fileOsTemplate")
    public OsTemplate fileOsTemplate(FileOsClient cosClient, OsConfigProperties osConfigProperties) {
        FileOsTemplate osTemplate = new FileOsTemplate();
        //放入客户端
        osTemplate.setClient(cosClient);
        OperatingSystemEnum operatingSystem = IOUtil.getOperatingSystem();
        osConfigProperties.FileProperties fileProperties =osConfigProperties.getFile();
        //获取对应的根路径
        String bucketName = null;
        String cdnHost = null;
        if (fileProperties != null) {
            if (OperatingSystemEnum.WINDOWS.equals(operatingSystem)) {
                bucketName = fileProperties.getWindowsRootPath();
            } else if (OperatingSystemEnum.MAC.equals(operatingSystem)) {
                bucketName = fileProperties.getMacRootPath();
            } else {
                bucketName = fileProperties.getLinuxRootPath();
            }
            cdnHost = fileProperties.getCdnHost();
        }
        if (bucketName == null) {
            log.warn("File root path is null. use '/' is root");
            //默认是根路径
            bucketName = "/";
        }
        if (cdnHost == null) {
            log.warn("File cdnHost is null.");
        }
        osTemplate.setCdnHost(cdnHost);

        //设置存储名字
        osTemplate.setBucketName(bucketName);
        //配置文件
        osTemplate.setOsConfigProperties(osConfigProperties);
        return osTemplate;
    }
}

2.6 工具类

@Slf4j
public abstract class IOUtil {


    /**
     * 输入流转换为输出流
     *
     * @param is 输入流
     * @return 输出流
     */
    public static ByteArrayOutputStream convertInputStream(InputStream is) {
        final byte[] by = new byte[1024];
        ByteArrayOutputStream data = new ByteArrayOutputStream();
        try {
            // 将内容读取内存中
            int len;
            while ((len = is.read(by)) != -1) {
                data.write(by, 0, len);
            }
        } catch (IOException e) {
            log.error("convertInputStream IO Exception", e);
        }
        return data;
    }

    /**
     * base64转化为byte[]字节流
     *
     * @param base64Str 照片的base64
     * @return 字节流
     */
    public static byte[] base64ToBytes(String base64Str) {
        if (StringUtils.isBlank(base64Str)) {
            throw new SealOsException("base64Str is blank!");
        }
        return Base64.decodeBase64(base64Str);
    }

    /**
     * 获取文件输入流
     *
     * @param file 文件对象
     * @return 文件输入流对象
     */
    public static InputStream getInputStream(File file) {
        FileInputStream fin;
        try {
            fin = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            if (log.isDebugEnabled()) {
                log.debug(String.valueOf(e));
            }
            String msg = "找不到指定的文件[" + file.getName() + "]。";
            if (log.isDebugEnabled()) {
                log.debug(msg);
            }
            throw new SealOsException(msg, e);
        }
        return fin;
    }

    /**
     * 获取当前系统的操作系统类型
     */
    public static OperatingSystemEnum getOperatingSystem() {
        if (System.getProperty("os.name").toUpperCase().contains("WINDOWS")) {
            return OperatingSystemEnum.WINDOWS;
        } else if (System.getProperty("os.name").toUpperCase().contains("MAC")) {
            return OperatingSystemEnum.MAC;
        } else {
            return OperatingSystemEnum.LINUX;
        }
    }

    /**
     * 文件后缀
     *
     * @param fileName 文件名或者文件路径
     * @return 后缀类型
     */
    public static String getFileSuffix(String fileName) {
        if (fileName == null) {
            return null;
        }
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 获取文件名
     */
    public static String getFileName(String filePath) {
        if (filePath == null) {
            return null;
        }
        File file=new File(filePath);
        return file.getName();
    }

    /**
     * 获取目录
     */
    public static String getDirName(String filePath) {
        if (filePath == null) {
            return null;
        }
        File file=new File(filePath);
        return file.getParent();
    }


}
public abstract class OkHttpUtil {

    /**
     * 最大连接时间
     */
    public final static int CONNECTION_TIMEOUT = 15;

    public final static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class);

    /**
     * client
     * 配置重试
     */
    private final static OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
            .build();

    /**
     * get请求,无需转换对象
     *
     * @param url 链接
     * @return 响应信息
     */
    public static ByteArrayOutputStream get(String url) {
        try {
            Request request = new Request.Builder().url(url).build();
            Response response = HTTP_CLIENT.newCall(request).execute();

            if (response.isSuccessful() && response.body() != null) {
                InputStream inputStream = response.body().byteStream();
                return IOUtil.convertInputStream(inputStream);
            }
        } catch (Exception e) {
            logger.error("执行get请求,url: {} 失败!", url, e);
            throw new SealOsException(e);
        }
        return null;
    }
}

2.7 异常类

public class OsException extends RuntimeException {


    public OsException() {
    }

    public OsException(String message) {
        super(message);
    }

    public OsException(String message, Throwable cause) {
        super(message, cause);
    }

    public OsException(Throwable cause) {
        super(cause);
    }

    public OsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容