前言
上一篇文章整理了阿里云对象存储相关笔记,这一片文章学习下使用七牛云对象存储,通过这篇文章你将了解到关于七牛云对象存储中几个方面知识点:
- 七牛云文件上传封装
- 七牛云文件下载封装
- 七牛云资源管理模块
- 图片鉴别
- CDN处理等
1.1 七牛云返回魔法变量
七牛云在设置获取上传凭证的时候设置StringMap参数例如:
policyMap.put("returnBody", "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\",\"fsize\":$(fsize)}");
// 表示在上传等一系列操作中,返回的数据中包含这几项数据
可以根据七牛云的文档上设置返回其他我们需要的参数: 七牛云returnbody返回变量设置
1.2 七牛云设置callback
七牛云中还可以设置callback等异步回调:七牛云设置callback
需要回调本地方法有几个条件:
1.2.1 callbackUrl 设置:
上传成功后,七牛云向业务服务器发送 POST 请求的 URL。必须是公网上可以正常进行 POST 请求并能响应 HTTP/1.1 200 OK 的有效 URL。另外,为了给客户端有一致的体验,我们要求 callbackUrl 返回包 Content-Type 为 “application/json”,即返回的内容必须是合法的 JSON 文本。出于高可用的考虑,本字段允许设置多个 callbackUrl(用英文符号 ; 分隔),在前一个 callbackUrl 请求失败的时候会依次重试下一个callbackUrl。一个典型例子是:http://
/callback;http:// /callback,并同时指定下面的 callbackHost 字段。在 callbackUrl 中使用 ip 的好处是减少对 dns 解析的依赖,可改善回调的性能和稳定性。指定 callbackUrl,必须指定 callbackbody,且值不能为空
1.2.2 callbackBody设置:
上传成功后,七牛云向业务服务器发送 Content-Type: application/x-www-form-urlencoded 的 POST 请求。业务服务器可以通过直接读取请求的 query 来获得该字段,支持魔法变量和自定义变量。callbackBody 要求是合法的 url query string。例如key=$(key)&hash=$(etag)&w=$(imageInfo.width)&h=$(imageInfo.height)。如果callbackBodyType指定为application/json,则callbackBody应为json格式,例如:{“key”:”$(key)”,”hash”:”$(etag)”,”w”:”$(imageInfo.width)”,”h”:”$(imageInfo.height)”}。
1.2.3 callbackBodyType设置:
上传成功后,七牛云向业务服务器发送回调通知 callbackBody 的 Content-Type。默认为 application/x-www-form-urlencoded,也可设置为 application/json。
1.2.3 可以自己定义变量:
写法{“x:x”:{y}} 定义了x变量
1.3 七牛云上传封装
1.3.1 七牛云普通文件上传
/**
* 直接上传file 文件
* @param fileParam
* @return
*/
public static BaseResponse uploadFile(FileParam fileParam){
try {
// 基于基类方法获取上传uploadManager()
UploadManager uploadManager = getUploadManager();
// 上传方法
Response response = uploadManager.put(fileParam.getFile(), fileParam.getFileName(), getUpToken(fileParam.getBucket()));
LOGGER.debug("上传七牛云反馈:{}",response.bodyString());
return BaseResponse.success(response);
} catch (QiniuException e) {
LOGGER.error("文件{} 上传失败,失败原因:",fileParam.getFileName(),e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.3.2 上传输入流
/**
* 通过流的方式上传文件
* @param inputStreamParam
* @return
*/
public static BaseResponse uploadFileByInputStream(InputStreamParam inputStreamParam){
UploadManager uploadManager = getUploadManager();
try {
Response response = uploadManager.put(inputStreamParam.getInputStream(), inputStreamParam.getFileName(), getUpToken(inputStreamParam.getBucket()),null,null);
LOGGER.debug("上传七牛云反馈:{}",response.bodyString());
return BaseResponse.success(response);
} catch (QiniuException e) {
LOGGER.error("文件{} 上传失败,失败原因:",inputStreamParam.getInputStream(),e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.3.3 封装异步上传文件
/**
* 异步上传 byte[] 数据
* @param byteParam byteParam
* @param handler 异步处理成功调用方法(客户端需要实现这个接口中的方法)
*/
public static BaseResponse asyncPutData(ByteParam byteParam, UpCompletionHandler handler){
UploadManager uploadManager = getUploadManager();
try {
uploadManager.asyncPut(byteParam.getData(),byteParam.getFileName(),getUpToken(byteParam.getBucket()),null,null, false,handler);
return BaseResponse.success(null);
} catch (IOException e) {
return BaseResponse.faild(e.getMessage());
}
}
1.3.4 封装callBack
/**
* 调用七牛云设置回调接口
* @param fileParam 上传file文件
* @param policy 设置回调参数
* "callbackUrl": "<RequestUrlForAppServer string>",
* "callbackHost": "<RequestHostForAppServer string>",
* "callbackBody": "<RequestBodyForAppServer string>",
* "callbackBodyType": "<RequestBodyTypeForAppServer string>",
* @return
*/
public static BaseResponse putFileCallBack(FileParam fileParam,StringMap policy){
String policyToken = getPolicyToken(policy,fileParam.getBucket());
UploadManager uploadManager = getUploadManager();
try {
Response response = uploadManager.put(fileParam.getFile(), fileParam.getFileName(), policyToken);
LOGGER.debug("上传文件回调 {}",response.bodyString());
return BaseResponse.success(response);
} catch (QiniuException e) {
LOGGER.error("文件{} 上传失败,失败原因:",fileParam.getFileName(),e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.3.5 断点续传(会在本地生成一个记录文件记录上传的一些信息)
/**
* 断点续传 (record 记录放在系统临时文件夹下)
* @param fileParam 文件信息
* @return
*/
public static BaseResponse resumeFile(FileParam fileParam){
String recordPath = System.getProperty("java.io.tmpdir");
UploadManager uploadManager = getResumeUploadManager(recordPath);
try {
Response response = uploadManager.put(fileParam.getFile(), fileParam.getFileName(), getUpToken(fileParam.getBucket()));
return BaseResponse.success(response);
} catch (QiniuException e) {
e.printStackTrace();
LOGGER.error("断点续传出错 {}",e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.3.6 分片上传(将上传的文件分成4M一个块,按照块来上传)
/**
* 使用ResumeUploader 实现分块上传
* 通过线程异步上传,守护线程监控上传进度
* @param fileParam 文件参数类
*/
public static void resumeUpload(FileParam fileParam){
String recordPath = System.getProperty("java.io.tmpdir");
try {
final FileRecorder fileRecorder = new FileRecorder(recordPath);
final String recorderKey = fileRecorder.recorderKeyGenerate(fileParam.getFileName(), fileParam.getFile());
final ResumeUploader resumeUploader = getResumeUploader(fileRecorder, fileParam);
Thread thread = new Thread(){
@Override
public void run() {
try {
resumeUploader.upload();
} catch (QiniuException e) {
e.printStackTrace();
}
}
};
thread.start();
Thread record = new Thread(){
@Override
public void run() {
while(true){
showRecord("上传文件", fileRecorder, recorderKey);
}
}
};
record.setDaemon(true);
record.start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 显示上传进度
* @param pre
* @param recorder
* @param recordKey
*/
private static void showRecord(String pre, FileRecorder recorder, String recordKey) {
try {
byte[] data = recorder.get(recordKey);
if (data != null) {
String jsonString = new String(data);
if( jsonString.trim() !=""){
JSONObject object = JSONObject.parseObject(new String(data), JSONObject.class);
Long offset = object.getLong("offset");
Long size = object.getLong("size");
BigDecimal divide = new BigDecimal(offset).divide(new BigDecimal(size),4,BigDecimal.ROUND_HALF_UP);
if(divide.intValue() == 1){
recorder.del(recordKey);
}
Object modify_time1 = concurrentMap.get(offset);
if(modify_time1 ==null){
LOGGER.debug("更新一次{}",offset);
LOGGER.debug("{},上传进度{} %",pre,divide.multiply(new BigDecimal(100)));
concurrentMap.put(offset,offset);
}
}
}
} catch (Exception e) {
LOGGER.error("展示进度信息报错{}",e.getMessage());
}
}
1.4 七牛云下载链接封装
1.4.1 获取存储空间上的下载链接
/**
* 生成文件的下载链接
*
* @param downloadParam
* @return
*/
public static String getDownloadFileUrl(DownloadParam downloadParam) {
try {
String fileName = downloadParam.getIsFileNameEncoded() ? downloadParam.getFileName()
: URLEncoder.encode(downloadParam.getFileName(), "UTF-8");
String publicFileUrl =
String.format("%s/%s", downloadParam.getDomainOfBucket(), fileName);
if (!downloadParam.getPrivate()) {
//公有空间直接返回地址
return publicFileUrl;
}
return getAuth().privateDownloadUrl(publicFileUrl, downloadParam.getExpireInSeconds());
} catch (UnsupportedEncodingException e) {
logger.error("fail to get download file url with downloadVo = {}", downloadParam.toString());
e.printStackTrace();
}
return null;
}
1.4.2 将七牛云上的多个文件作为压缩文件
/**
* 将七牛云上的多个文件作为压缩文件 下载
*
* @param zipFilePath filePath 生成压缩包路径
* @param downloadVos
*/
public static void compressToZipFiles(String zipFilePath, DownloadParam... downloadVos) {
if (CollectionUtils.isEmpty(downloadVos)) {
logger.debug("downloadVos is null");
return;
}
try {
FileOutputStream fileOutputStream = new FileOutputStream(zipFilePath);
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
for (DownloadParam downloadVo : downloadVos) {
String fileUrl = getDownloadFileUrl(downloadVo);
if (fileUrl == null) {
continue;
}
ZipEntry entry = new ZipEntry(getRealFileName(downloadVo.getFileName()));
zipOutputStream.putNextEntry(entry);
int len = 0;
byte[] buffer = new byte[2048];
URL url = new URL(fileUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10 * 1000);
InputStream is = conn.getInputStream();
while ((len = is.read(buffer)) != -1) {
zipOutputStream.write(buffer, 0, len);
zipOutputStream.flush();
}
is.close();
zipOutputStream.close();
}
} catch (Exception e) {
logger.error("fail to compress file to zip:{}", e);
}
}
1.4.3 生成下载二维码
例如:http://p73pu13b2.bkt.clouddn.com/vedio.mp4?qrcode
1.5 七牛云资源管理模块
1.5.1 获取文件信息
/**
* 获得文件信息
* @param bucket 存储空间
* @param key 文件名称
* @return
*/
public static FileInfo getFileInfo(String bucket,String key){
if(StringUtils.isNullOrEmpty(bucket)){
LOGGER.debug("bucket is not null");
return null;
}
try {
BucketManager bucketManager = getBucketManager();
return bucketManager.stat(bucket,key);
} catch (QiniuException e) {
e.printStackTrace();
LOGGER.error("获得文件信息报错");
}
return null;
}
1.5.2 修改文件的mimeType类型
/**
* 修改文件的mimeType 类型
* @param bucket 存储空间
* @param key 文件key
* @param newMimeType 文件mimeType 类型
*/
public static BaseResponse changeMimeType(String bucket, String key, String newMimeType ){
if(StringUtils.isNullOrEmpty(bucket)){
LOGGER.debug("bucket is not null");
return BaseResponse.faild("bucket is not null");
}
try {
BucketManager bucketManager = getBucketManager();
Response response = bucketManager.changeMime(bucket, key, newMimeType);
return BaseResponse.success(response);
} catch (QiniuException e) {
e.printStackTrace();
return BaseResponse.faild(e.getMessage());
}
}
1.5.3 复制文件
/**
* 相同账号下一个存储空间的文件向另一个存储空间复制
* @param fromBucket 来自于哪一个存储空间
* @param toBucket 复制到指定存储空间
* @param fromKey 复制文件名称
* @param toKey 复制到另一个空间文件名称
* @param force 如果空间有相同名称的文件则强制覆盖
*/
public static BaseResponse copy(String fromBucket, String toBucket, String fromKey, String toKey, boolean force){
if(CollectionUtils.isEmpty(fromBucket,toBucket,fromKey,toKey)){
LOGGER.error("复制参数不能为空");
return BaseResponse.faild("复制参数不能为空");
}
try {
BucketManager bucketManager = getBucketManager();
Response response = bucketManager.copy(fromBucket, fromKey, toBucket, toKey, force);
return BaseResponse.success(response);
} catch (QiniuException e) {
LOGGER.error("复制文件出错{}",e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.5.4 移动文件
/**
* 相同账号下 移动不同存储空间的文件
* @param fromBucket 来自于哪一个存储空间
* @param toBucket 复制到指定存储空间
* @param fromKey 复制文件名称
* @param toKey 复制到另一个空间文件名称
* @param force 如果空间有相同名称的文件则强制覆盖
*/
public static BaseResponse move(String fromBucket,String fromKey,String toBucket,String toKey,boolean force){
if(CollectionUtils.isEmpty(fromBucket,fromKey,toBucket,toKey)){
LOGGER.error("移动文件参数不能为空");
return BaseResponse.faild("移动文件参数不能为空");
}
try {
BucketManager bucketManager = getBucketManager();
Response response = bucketManager.move(fromBucket, fromKey, toBucket, toKey, force);
return BaseResponse.success(response);
} catch (QiniuException e) {
LOGGER.error("移动文件出错{}",e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.5.5 删除文件
/**
* 删除 一个bucket 下的文件
* @param key 文件名称
* @param bucket 存储空间
*/
public static BaseResponse deleteFile(String bucket, String key){
if(CollectionUtils.isEmpty(bucket,key)){
LOGGER.error("删除文件时,bucket 和 key 不能为空");
return BaseResponse.faild("参数异常");
}
try {
BucketManager bucketManager = getBucketManager();
Response response = bucketManager.delete(bucket, key);
LOGGER.debug("七牛云删除反馈{}",response.bodyString());
return BaseResponse.success(response);
} catch (QiniuException e) {
LOGGER.error("异常信息{}",e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.5.6 重命名文件
/**
* 重命名文件
* @param bucket
* @param oldKey
* @param newKey
* @param force
*/
public static BaseResponse rename(String bucket,String oldKey,String newKey,boolean force){
if(CollectionUtils.isEmpty(bucket,oldKey,newKey)){
LOGGER.error("重命名参数不合法");
return BaseResponse.faild("参数不合法");
}
try {
BucketManager bucketManager = getBucketManager();
Response rename = bucketManager.rename(bucket, oldKey, newKey, force);
return BaseResponse.success(rename);
} catch (QiniuException e) {
LOGGER.error("七牛云重命名错误{}",e.getMessage());
return BaseResponse.faild(e.getMessage());
}
}
1.6 图片鉴别
1.6.1 图片鉴黄
/**
* 鉴黄识别
* @param imageUrl bucket 内的图片
* @return response
*/
public static String identificationImagePlup(String imageUrl){
if(StringUtils.isNullOrEmpty(imageUrl)){
logger.debug("imageUrl is null");
return null;
}
imageUrl = imageUrl+"?"+ Constant.QPULP;
try {
return getClient().get(imageUrl).bodyString();
} catch (QiniuException e) {
e.printStackTrace();
logger.error("鉴黄识别出错了{}",e.getMessage());
}
return null;
}
1.6.2 图片鉴别暴恐识别
/**
* 鉴别暴恐识别
* @param imageUrl 图片路径
* @return
*/
public static String identificationImageTerror(String imageUrl){
if(StringUtils.isNullOrEmpty(imageUrl)){
logger.debug("imageUrl is null");
return null;
}
imageUrl = imageUrl +"?"+Constant.QTERROR;
try {
return getClient().get(imageUrl).bodyString();
} catch (QiniuException e) {
e.printStackTrace();
logger.error("鉴别暴恐识别出错了{}",e.getMessage());
}
return null;
}
1.6.3 图片鉴别是否为政治人物
/**
* 鉴别是否为政治人物
* @param imageUrl 图片路径
* @return
*/
public static String identificationImagePolitician(String imageUrl){
if(StringUtils.isNullOrEmpty(imageUrl)){
logger.debug("imageUrl is null");
return null;
}
imageUrl = imageUrl +"?"+Constant.QPOLITICIAN;
try {
return getClient().get(imageUrl).bodyString();
} catch (QiniuException e) {
e.printStackTrace();
logger.error("鉴别是否为政治人物出错了{}",e.getMessage());
}
return null;
}
1.7 CDN处理
1.7.1 cdn 更新urls
/**
* cdn 更新urls
* 单次方法调用刷新的链接不可以超过100个
* @param urls 待刷新的链接列表
* @return
*/
public static CdnResult.RefreshResult refreshUrls(String[] urls){
if(CollectionUtils.isEmpty(urls)){
logger.debug("param is null");
return null;
}
CdnManager cdnManager = getCdnManager();
try {
return cdnManager.refreshUrls(urls);
} catch (QiniuException e) {
logger.error("cdn 刷新连接{}",e.getMessage());
return null;
}
}
1.7.2 cdn 刷新目录
/**
* cdn 刷新目录
* 单次方法调用刷新的目录不可以超过10个,另外刷新目录权限需要联系技术支持开通
* @param dirs 待刷新的目录列表,目录必须以 / 结尾
* @return BaseResponse
*/
public static CdnResult.RefreshResult refreshDirs(String[] dirs){
if(CollectionUtils.isEmpty(dirs)){
logger.debug("param is null");
return null;
}
CdnManager cdnManager = getCdnManager();
try {
return cdnManager.refreshDirs(dirs);
} catch (QiniuException e) {
logger.error("cdn 刷新目录{}",e.getMessage());
return null;
}
}
1.7.3 获取流量
/**
* TODO 这个返回参数需要再封装
* 获取某个时间段的流量
* @param domains 域名列表
* @param fromDate 时间起始
* @param toDate 截止到什么时间
* @param granularity 时间粒度(数据粒度,支持的取值为 5min/hour /day)
* @return
*/
public static Map<String, CdnResult.FluxData> getFluxData(String[] domains, String fromDate, String toDate, String granularity){
if(CollectionUtils.isEmpty(domains)){
logger.debug("domains is null");
return null;
}
try {
CdnManager cdnManager = getCdnManager();
CdnResult.FluxResult fluxResult = cdnManager.getFluxData(domains, fromDate, toDate, granularity);
return fluxResult.data;
} catch (QiniuException e) {
e.printStackTrace();
logger.error("获取七牛云域名出错了{}",e.getMessage());
}
return null;
}
1.7.4 获取带宽
/**
* 获取域名下的带宽
* @param domains 域名列表
* @param fromDate 起始时间
* @param toDate 结束时间
* @param granularity 时间粒度 数据粒度,支持的取值为 5min/hour /day)
* @return
*/
public static Map<String, CdnResult.BandwidthData> getBandwidthData(String[] domains, String fromDate, String toDate, String granularity){
if(CollectionUtils.isEmpty(domains)){
logger.debug("domains is null");
return null;
}
try {
CdnManager cdnManager = getCdnManager();
CdnResult.BandwidthResult bandwidthData = cdnManager.getBandwidthData(domains, fromDate, toDate, granularity);
return bandwidthData.data;
} catch (QiniuException e) {
e.printStackTrace();
logger.error("获取域名下的宽带出错了{}",e.getMessage());
}
return null;
}
1.7.5 获取日志下载链接
/**
* 获取域名下 某一天的日志下载链接
* @param domains 域名列表
* @param logDate 具体的某一天时间
* @return
*/
public static Map<String, CdnResult.LogData[]> getCdnLogList(String[] domains,String logDate){
if(CollectionUtils.isEmpty(domains) || StringUtils.isNullOrEmpty(logDate)){
logger.debug("param is null");
return null;
}
try {
CdnManager cdnManager = getCdnManager();
CdnResult.LogListResult cdnLogList = cdnManager.getCdnLogList(domains, logDate);
return cdnLogList.data;
} catch (QiniuException e) {
e.printStackTrace();
logger.error("获取日志链接出错{}",e.getMessage());
}
return null;
}