创建ceph-starter自动化工程:
创建一个spring boot工程,作为一个公用组件。
pom文件依赖:
dependencies>
<!-- Spring Boot 自定义启动器的依赖 -->
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-actuator-autoconfigureartifactId>
dependency>
<!-- ceph 依赖 -->
dependency>
groupId>com.cephgroupId>
artifactId>radosartifactId>
version>0.6.0version>
dependency>
<!-- ceph fs 操作依赖 -->
dependency>
groupId>com.cephgroupId>
artifactId>libcephfsartifactId>
version>0.80.5version>
dependency>
<!-- ceph swift 依赖 -->
dependency>
groupId>org.javaswiftgroupId>
artifactId>jossartifactId>
version>0.10.2version>
dependency>
dependencies>
直接采用目前的最新版, 加入Ceph相关的三个依赖。
代码实现
封装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();
CollectionContainer> 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 接口操作实例。
自动化配置:
要让自定义Ceph Starter真正生效, 必须遵循Spring boot 的SPI扩展机制, 在resources环境中, META-INF目录下, 创建spring.factories文件:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.ceph.starter.AutoCephSwiftConfiguration
指定我们上面所写的自动化配置类。
工程配置
application.yml
server:
port: 10692
spring:
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
POM依赖配置:
dependencies>
<!-- spring boot 依赖 -->
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-webartifactId>
dependency>
<!-- Spring boot thymeleaf 模板依赖 -->
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<!-- Ceph 自动化封装组件 -->
dependency>
groupId>com.itcast.cephgroupId>
artifactId>ceph-starterartifactId>
version>1.0-SNAPSHOTversion>
dependency>
dependencies>
加入Ceph 自动化封装组件配置依赖。
实现文件上传接口:
/**
* 上传用户文件
* @return
*/
public String uploadUserFile(MultipartFile file) throws Exception {
// 获取唯一文件ID标识
String remoteFileId = globalIDGenerator.nextStrId();
// 上传文件至CEPH
cephSwiftOperator.createObject(remoteFileId, file.getBytes());
return remoteFileId;
}
Controller层实现:
在UserManagerController下面, 增加上传接口:
/**
* 用户文件上传
* @param file
* @return
*/
@PostMapping("/upload")
@ResponseBody
public 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, 非直接数据流方式上传, 不然服务不能正常识别接收文件。
新增一个接口, 根据上传的文件ID标识下载文件。
Service层:
实现下载用户文件接口:
/**
* 下载用户文件
* @param fileId
* @return
* @throws Exception
*/
public InputStream downloadUserFile(String fileId) throws Exception {
return cephSwiftOperator.retrieveObject(fileId);
}
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;
}
访问上传页面
上传成功后, 会返回文件ID:
下载文件:
输入文件ID进行下载:
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4295/viewspace-2797823/ ,如需转载,请注明出处,否则将追究法律责任。