1. Ceph封装与自动化拆卸

  1. 创立ceph-starter自动化工程:

    创立一个spring boot工程,作为一个专用组件。
  2. pom文件依赖:

    <dependencies>        <!-- Spring Boot 自定义启动器的依赖 -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-actuator-autoconfigure</artifactId>        </dependency>        <!-- ceph 依赖 -->        <dependency>            <groupId>com.ceph</groupId>            <artifactId>rados</artifactId>            <version>0.6.0</version>        </dependency>        <!-- ceph fs 操作依赖 -->        <dependency>            <groupId>com.ceph</groupId>            <artifactId>libcephfs</artifactId>            <version>0.80.5</version>        </dependency>        <!-- ceph swift 依赖 -->        <dependency>            <groupId>org.javaswift</groupId>            <artifactId>joss</artifactId>            <version>0.10.2</version>        </dependency>    </dependencies>

    间接采纳目前的最新版, 退出Ceph相干的三个依赖。

  3. 代码实现

    封装Ceph操作接口, CephSwiftOperator类:

    public class CephSwiftOperator {    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());    /**     * 用户名     */    private String username;       /**     * 明码     */    private String password;    /**     * 认证接入地址     */    private String authUrl;    /**     * 默认容器名称     */    private String defaultContainerName;    /**     * Ceph账户对象     */    private Account account;    /**     * Ceph容器对象     */    private Container container;       public CephSwiftOperator(String username, String password, String authUrl, String defaultContainerName) {        // 初始化配置信息        this.username = username;        this.password = password;        this.authUrl = authUrl;        this.defaultContainerName = defaultContainerName;        init();       }    /**     * 初始化建设连贯     */    public void init() {        try {            // Ceph用户认证配置            AccountConfig config = new AccountConfig();            config.setUsername(username);            config.setPassword(password);            config.setAuthUrl(authUrl);            config.setAuthenticationMethod(AuthenticationMethod.BASIC);            account = new AccountFactory(config).createAccount();            // 获取容器            Container newContainer = account.getContainer(defaultContainerName);            if (!newContainer.exists()) {                container = newContainer.create();                log.info("account container create ==> " + defaultContainerName);            } else {                container = newContainer;                log.info("account container exists!  ==> " + defaultContainerName);            }        }catch(Exception e) {            // 做异样捕捉, 防止服务不能失常启动            log.error("Ceph连贯初始化异样: " + e.getMessage());        }    }       /**     * 上传对象     * @param remoteName     * @param filepath     */    public void createObject(String remoteName, String filepath) {        StoredObject object = container.getObject(remoteName);        object.uploadObject(new File(filepath));    }    /**     * 上传文件对象(字节数组模式)     * @param remoteName     * @param inputStream     */    public void createObject(String remoteName, byte[] inputStream) {        StoredObject object = container.getObject(remoteName);        object.uploadObject(inputStream);    }    /**     * 获取指定对象     * @param containerName     * @param objectName     * @param outpath     */    public void  retrieveObject(String objectName,String outpath){        StoredObject object = container.getObject(objectName);        object.downloadObject(new File(outpath));    }   /**     * 下载文件, 转为文件流模式     * @param objectName     * @return     */    public InputStream retrieveObject(String objectName){        StoredObject object = container.getObject(objectName);        return object.downloadObjectAsInputStream();    }       /**     * 删除指定文件对象     * @param containerName     * @param objectName     * @return     */    public boolean deleteObject(String objectName){        try {            StoredObject object = container.getObject(objectName);            object.delete();            return !object.exists();        }catch(Exception e) {            log.error("Ceph删除文件失败: " + e.getMessage());        }        return false;    }    /**     * 获取所有容器     * @return     */    public List listContainer() {        List list = new ArrayList();        Collection<Container> containers = account.list();        for (Container currentContainer : containers) {            list.add(currentContainer.getName());            System.out.println(currentContainer.getName());        }        return list;    }   }   
    • 此封装接口将纳入Spring 容器治理, 即为单例, 构造函数会初始化Ceph认证连贯等信息
    • 将Account 与Container 设为成员变量, 便于复用, 缩小开销。
    • 初始化会默认一个容器名称, 个别每个服务设置一个容器名称, 如果业务性能比拟庞杂, 能够每个业务模块设置一个容器。
    • Swift Api曾经做了较欠缺的封装, 咱们外部应用比较简单, 次要封装上传和下载接口, 为便于调用解决, 做了进一步封装。

    AutoCephSwiftConfiguration自动化配置类:

    @Configuration@EnableAutoConfiguration@ConditionalOnProperty(name = "ceph.authUrl")public class AutoCephSwiftConfiguration {       @Value("${ceph.username}")    private String username;    @Value("${ceph.password}")    private String password;    @Value("${ceph.authUrl}")    private String authUrl;    @Value("${ceph.defaultContainerName}")    private String defaultContainerName;      @Bean    public CephSwiftOperator cephSwiftOperator() {        return new CephSwiftOperator(username, password, authUrl, defaultContainerName);    }}

    ConditionalOnProperty依据ceph.authUrl属性来决定是否加载配置,如果配置文件中没有设置Ceph相干属性, 即便maven中援用, 启动也不会报错。 该自动化配置, 负责初始化一个Ceph Swift 接口操作实例。

  4. 自动化配置:

    要让自定义Ceph Starter真正失效, 必须遵循Spring boot 的SPI扩大机制, 在resources环境中, META-INF目录下, 创立spring.factories文件:

    # Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.ceph.starter.AutoCephSwiftConfiguration

    指定咱们下面所写的自动化配置类。

2. 创立用户治理工程

  1. 工程配置
    application.yml

    server:  port: 10692spring:  application:    name: user-manager  # 模板配置  thymeleaf:    prefix: classpath:/templates/    suffix: .html    mode: HTML    encoding: utf-8    servlet:      content-type: text/html  # 文件上传大小限度  servlet:    multipart:      max-file-size: 100MB      max-request-size: 100MB# ceph swift 认证信息配置ceph:  username: cephtester:subtester  password: 654321  authUrl: http://10.10.20.11:7480/auth/1.0  defaultContainerName: user_datainfo
  2. POM依赖配置:

       <dependencies>       <!-- spring boot 依赖 -->       <dependency>           <groupId>org.springframework.boot</groupId>           <artifactId>spring-boot-starter-web</artifactId>       </dependency>          <!-- Spring boot thymeleaf 模板依赖 -->       <dependency>           <groupId>org.springframework.boot</groupId>           <artifactId>spring-boot-starter-thymeleaf</artifactId>       </dependency>          <!-- Ceph 自动化封装组件 -->       <dependency>           <groupId>com.itcast.ceph</groupId>           <artifactId>ceph-starter</artifactId>           <version>1.0-SNAPSHOT</version>       </dependency>      </dependencies>

    退出Ceph 自动化封装组件配置依赖。

3. Ceph文件上传实现

  1. 实现文件上传接口:

    /** * 上传用户文件 * @return */public String uploadUserFile(MultipartFile file) throws Exception {    // 获取惟一文件ID标识    String remoteFileId = globalIDGenerator.nextStrId();    // 上传文件至CEPH    cephSwiftOperator.createObject(remoteFileId, file.getBytes());    return remoteFileId;}
  2. Controller层实现:

    在UserManagerController上面, 减少上传接口:

    /** * 用户文件上传 * @param file * @return */@PostMapping("/upload")@ResponseBodypublic String upload(@RequestParam("file") MultipartFile file) {    String  result = null;    try {        // 通过Ceph Swift上传文件        String userFileId = userManagerService.uploadUserFile(file);        result = "上传的文件ID: " + userFileId;    }catch(Exception e) {        log.error(e.getMessage(), e);        result = "出现异常:" + e.getMessage();    }    return result;}   

    通过Spring boot实现文件上传, 留神以表单form模式提交, 类型为multipart/form-data, 参数名为file, 非间接数据流形式上传, 不然服务不能失常辨认接管文件。

4. Ceph文件下载实现

新增一个接口, 依据上传的文件ID标识下载文件。

  1. Service层:

    实现下载用户文件接口:

    /** * 下载用户文件 * @param fileId * @return * @throws Exception */public InputStream downloadUserFile(String fileId) throws Exception {    return cephSwiftOperator.retrieveObject(fileId);}
  2. Controller层:

    /** * 依据文件ID下载用户文件信息 * @param filename * @return */@RequestMapping(value = "/download")public String downloadFile(@NotBlank(message = "文件ID不能为空!") String filename, HttpServletResponse response){       String result = null;       // 文件流缓存    BufferedInputStream bis = null;    // 文件输入流    OutputStream os = null;    try {        // 1. 从Ceph服务器上获取文件流        InputStream inputStream = userManagerService.downloadUserFile(filename);        // 2.设置强制下载, 不间接关上        response.setContentType("application/x-msdownload");        // 3. 设置下载的文件名称        response.addHeader("Content-disposition", "attachment; fileName=" + filename);        // 4. 输入文件流        byte[] buffer = new byte[1024];        bis = new BufferedInputStream(inputStream);        os = response.getOutputStream();        int i = bis.read(buffer);        while(i != -1) {            os.write(buffer, 0, i);            i = bis.read(buffer);        }        os.flush();        return null;    }catch(Exception e) {        log.error(e.getMessage(), e);        result = "出现异常:" + e.getMessage();    }finally {        // 最初, 要记住敞开文件流        if(bis != null ) {            try {                bis.close();            } catch (IOException e) {                log.error(e.getMessage(), e);            }        }    }       return result;}

5. 性能验证

  1. 拜访上传页面

    地址: http://127.0.0.1:10692/user/file

     ![file](/img/bVcQ8Xw)
  2. 上传胜利后, 会返回文件ID:

     ![file](/img/bVcQ8Xx)
  3. 下载文件:

    输出文件ID进行下载:

     ![file](/img/bVcQ8Xy)

本文由mirson创作分享,如需进一步交换,请加QQ群:19310171或拜访www.softart.cn