大部分的软件或者网站都会有二进制数据(文件)需要存储,比如上传的附件,文中的图片等等。
这些数据可以存储在本地文件系统、远程文件系统(FTP、NFS等)、数据库、或者云服务(Dropbox、AWS S3、七牛云存储等)中。为了将存储 的实现和使用分离,通常我们需要建立一个统一的存储接口或者 API。这样,API 提供者专注于存储逻辑的实现,API 调用者则专注于其它业务 逻辑的实现,并在适当的位置调用存储接口即可。
本文以 Jive Binary Storage Provider 为例介绍存储接口的范例。Jive 是非常有名的商业软件,jive-eos 是其中的一个包,存储接口 (StorageProvider)就定义在这个包内。
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Map;
public interface StorageProvider{
boolean put(String key, byte data);
boolean put(String key, InputStream data);
boolean containsKey(String key);
ByteBuffer getBuffer(String key);
InputStream getStream(String key);
boolean delete(String key);
Iterable<String> getKeys();
String getNamespace();
}
调用该接口的示例
ApplicationContext context = ...;
StorageProvider sp = context.getBean("storageProvider", StorageProvider.class);
//存文件
String filename = ...;
InputStream data = new FileInputStream(new File(filename));
KeyFactory keyFactory = context.getBean("keyFactory", KeyFactory.class);
String key = keyFactory.generateStorageKey();
boolean success = sp.put(key, data);
//根据 key 获取文件的数据流
String key = ...;
InputStream data = sp.getStream(key);
//输出data
//从一个存储器迁移到另外一个存储器
//例如从本地文件系统迁移到七牛云存储
StorageProvider src = new FileStorageProvider(...);
StorageProvider dest = new QiniudnStorageProvider(...);
Set<String> keys = Sets.newHashSet(src.getKeys());
for(String key: keys){
dest.put(key, src.getStream(key));
}
简单的本地文件存储实现(FileStorageProvider)(注意,仅仅是示例,只列出代码且无优化)
public class FileStorageProvider implements StorageProvider{
private final File rootDirectory;
public FileStorageProvider(File rootDirectory){
this.rootDirectory = rootDirectory;
if(!rootDirectory.exists()){
rootDirectory.mkdirs();
}
}
public boolean put(String key, InputStream data){
File file = new File(rootDirectory, key + ".bin");
IOUtils.copy(data, new FileOutputStream(file));
return true;
}
public boolean containsKey(String key){
return new File(rootDirectory, key + ".bin").exists();
}
public InputStream getStream(String key){
File file = new File(rootDirectory, key + ".bin");
return new FileInputStream(file);
}
}
实际上,Jive EOS 官方实现的 FileStorageProvider 要复杂的多,健壮的多。另外,Jive 还实现了
将数据存储到数据库的 JdbcStorageProvider 和存储到 AWS S3 服务的 S3StorageProvider,有兴趣
的读者可以搜索相关内容。
云存储是目前互联网相当热火的概念,在项目中使用云存储服务也已经成为了很多互联网应用的选择。上述 AWS S3 就是老牌的云存储服务。国内的云存储服务也不少,基本上都提供了自身的API(SDK),下面以七牛 云存储为例,简要说说如何实现一个存储器可以将二进制数据存储到七牛云,或者从七牛云获取二进制数据。
(默认使用私有空间,不完整版,仅示例)
public class QiniudnStorageProvider implements StorageProvider{
private final String ACCESS_KEY = "<YOUR APP ACCESS_KEY>";
private final String SECRET_KEY = "<YOUR APP SECRET_KEY>"
private Mac mac;
private final String bucketName;
private final String domain;
public FileStorageProvider(String bucketName){
this.bucketName = bucketName;
this.domain = "http://" + bucketName + ".u.qiniudn.com";
Config.ACCESS_KEY = ACCESS_KEY;
Config.SECRET_KEY = SECRET_KEY;
mac = new Mac(Config.ACCESS_KEY, Config.SECRET_KEY);
}
public boolean put(String key, InputStream data){
File file = File.createTempFile();
IOUtils.copy(data, new FileOutputStream(file));
PutPolicy putPolicy = new PutPolicy(bucketName);
String uptoken = putPolicy.token(mac);
PutExtra extra = new PutExtra();
PutRet ret = IoApi.putFile(uptoken, key, file, extra);
return true;
}
public boolean containsKey(String key){
String url = makeDownloadURL(key);
HttpClient hc = new HttpClient();
HttpMethod m = new HeadMethod(url);
hc.executeMethod(m);
return m.getStatusCode() == 200;
}
public InputStream getStream(String key){
String url = makeDownloadURL(key);
HttpClient hc = new HttpClient();
HttpMethod m = new GetMethod(url);
hc.executeMethod(m);
if(m.getStatusCode() == 200){
return m.getResponseBodyAsStream();
}
return null;
}
private String makeDownloadURL(String key){
String baseUrl = URLUtils.makeBaseUrl(domain, key);
GetPolicy getPolicy = new GetPolicy();
return getPolicy.makeRequest(baseUrl, mac);
}
}
基本原理就是这样的,如有需要,可根据 Qiniu Java SDK 文档和 HttpClient 文档自行实现。
| Source |
|