您的位置:首页 > 其它

**关于阿里云oss图片批量上传问题解析**

2017-03-31 10:50 357 查看
关于阿里云oss图片批量上传问题解析

背景

最近在项目开发过程中遇到的问题,消耗了比较久的时间,过程曲折,虽然最终达到目的,但是鉴于各种常用的云数据在对象存储方面大同小异,所以记录一下。

理一下思路:

我们在调用第三方对象存储API的时候,通常需要解决以下问题:

1.考虑通用性
2.在调用API过程中需要的参数.
3.如何处理数据(使用方式、成功与失败)
4.调用过程中的异常处理


通用性

在整个App中好几个界面都涉及到图片上传,根据每个界面上传的特点,将其整合成了一个工具类。


参数

阅读阿里云的API以及SDK文档,找关键字,事实证明这样真不好,发现几个重要的字段:Endpoint、Bucket、STS、OssClient、KeyObject、Meta、data、OSSAsyncTask,我理解就是拿一个地址一层层找文件,完整的url长这样:

“http://” +BucketName+ “.” +END_POINT + “/” + ObjectKey

1.EndPoint:Oss区域地址 Bucket:里面的容器,放文件(Object)的地方,这里提一下bucketname
2.KeyObject:上传文件在oss中的名称
3.Meta:上传文件的自我描述,算是标记,为name-value类型 data:这个就是字面上的意思
4.data:字面上的意思
5.STS:鉴权方式,关于这个方式的安全性以及涉及到的参数及其含义详见OSS文档。
6.OssClient:看到Client其实你就能联想到网络请求
7.OSSAsyncTask:异步任务,最终我就是差点当在这里,
看漏一句话:该任务属于安全上传,适用于高并发执行...我居然写到末尾把它硬生生写成同步,捂脸...


数据处理

图片上传过程中,每张图片的上传进度,成功与失败的数据返回处理,
上传过程中可能会出现的问题:比如说正在上传的图片被用户删了之类的等等。


准备工作

1.下载jar包:aliyun-oss-sdk-android-2.3.0.jar、okhttp-3.x.x.jar 和 okio-1.x.x.jar,加入libs中
2.设置权限:在manifests.xml中加入:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>


代码片段:

public class PictureUpload {
private static PictureUpload pictureUpload;
/**
* 上传client
*/
OSS oss;
/**
* 上传次数
*/
int number;
/**
* 成功上传(本地文件名作为key,阿里云地址为value)
*/
Map<String, Object> success = new HashMap<>();
/**
* 失败上传(返回失败文件的本地地址)
*/
List<String> failure = new ArrayList<>();
/**
* 上传回调
*/
UploadListener uploadListener;
/**
* 上传任务列表
*/
List<OSSAsyncTask> ossAsyncTasks = new ArrayList<>();
/**
* 自动更新Token
*/
OSSCredentialProvider credetialProvider = new OSSFederationCredentialProvider() {
@Override
public OSSFederationToken getFederationToken() {
try {
//阿里鉴权请求
AliAuthRes res = new JsonDefaultRequest(AliAuthRes.class).POST("", null, SharePerefrence.getTokenKey());
return new OSSFederationToken(res.getAccessK
ca3d
eyId(), res.getAccessKeySecret(), res.getSecurityToken(), res.getExpiration());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};

/**
* 单列模式
*
* @return
*/
public static PictureUpload getInstance() {
if (pictureUpload == null) {
pictureUpload = new PictureUpload();
}
return pictureUpload;
}

/**
* 构造函数
*/
public PictureUpload() {
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(20 * 1000); // 连接超时,默认15秒
conf.setSocketTimeout(20 * 1000); // socket超时,默认15秒
conf.setMaxConcurrentRequest(9); // 最大并发请求书,默认5个
conf.setMaxErrorRetry(3); // 失败后最大重试次数,默认2次
oss = new OSSClient(MyApplication.getInstance(), HttpConfig.END_POINT, credetialProvider, conf);
}

/**
* 添加上传任务
*
* @param paths
* @param listener
*/
public void setDatas(final List<String> paths, UploadListener listener) {
this.uploadListener = listener;
ossAsyncTasks.clear();
number = 1;
success.clear();
failure.clear();
for (String path : paths) {
final File file = new File(path);
/**
* 阿里云上文件名称
*/
String objectKey = UUID.randomUUID().toString() + "_" + SharePerefrence.getTokenKey() + ".jpg";
/**
* 用户自定义参数
*/
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.addUserMetadata("filePath", file.getPath());
objectMetadata.addUserMetadata("fileName", file.getName());
objectMetadata.addUserMetadata("objectKey", objectKey);

PutObjectRequest put = new PutObjectRequest(HttpConfig.BUCKET_NAME, objectKey, file.getPath());
put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
@Override
public void onProgress(PutObjectRequest putObjectRequest, long currentSize, long totalSize) {

}
});
/**
* 上传任务
*/
OSSAsyncTask task;
task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
@Override
public void onSuccess(PutObjectRequest putObjectRequest, PutObjectResult putObjectResult) {
number++;
String aliPath = formAliPath(putObjectRequest);
success.put(putObjectRequest.getMetadata().getUserMetadata().get("fileName"), aliPath);
if (number == paths.size()) {
uploadListener.onUploadComplete(success, failure);
}
}

@Override
public void onFailure(PutObjectRequest putObjectRequest, ClientException e, ServiceException e1) {
number++;
failure.add(putObjectRequest.getMetadata().getUserMetadata().get("filePath"));
if (number == paths.size()) {
uploadListener.onUploadComplete(success, failure);
}
}
});
/**
* 添加到上传记录
*/
ossAsyncTasks.add(task);
}
}

/**
* 添加上传任务
* @param paths key:本地地址
* @param listener
*/
public void setDatas(final Map<String,Picture>paths, final UploadListener listener){
this.uploadListener=listener;
number=1;
success.clear();
failure.clear();
Iterator iterator = paths.keySet().iterator();
while (iterator.hasNext()){
final String path= (String) iterator.next();
final Picture picture=paths.get(path);
File file=new File(path);

String objectKey=UUID.randomUUID().toString()+"_"+SharePerefrence.getTokenKey()+".jpg";

ObjectMetadata objectMetadata=new ObjectMetadata();
objectMetadata.addUserMetadata("fileTag",picture.getTag());
objectMetadata.addUserMetadata("fileName",file.getName());
objectMetadata.addUserMetadata("filePath",file.getPath());
objectMetadata.addUserMetadata("objectKey", objectKey);

final PutObjectRequest put=new PutObjectRequest(HttpConfig.BUCKET_NAME,objectKey,file.getPath());
put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
@Override
public void onProgress(PutObjectRequest putObjectRequest, long l, long l1) {
//上传进度回调
}
});

OSSAsyncTask task;
task=oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
@Override
public void onSuccess(PutObjectRequest putObjectRequest, PutObjectResult putObjectResult) {
number++;
String aliPath = formAliPath(putObjectRequest);
picture.setUrl(aliPath);
picture.setName(putObjectRequest.getMetadata().getUserMetadata().get("fileName"));
success.put(putObjectRequest.getMetadata().getUserMetadata().get("filePath"),picture);
if (number==paths.size()){
uploadListener.onUploadComplete(success,failure);
}
}

@Override
public void onFailure(PutObjectRequest putObjectRequest, ClientException e, ServiceException e1) {
number++;
failure.add(putObjectRequest.getMetadata().getUserMetadata().get("filePath"));
if (number==paths.size()){
uploadListener.onUploadComplete(success,failure);
}
}
});
ossAsyncTasks.add(task);
}

}

public void cancleTasks() {
for (OSSAsyncTask task : ossAsyncTasks) {
if (task.isCompleted()) {

}else {
task.cancel();
}
}
}

/**
* 拼接远程访问地址
*
* @param putObjectRequest
* @return
*/
private String formAliPath(PutObjectRequest putObjectRequest) {
return "http://" + putObjectRequest.getBucketName() + "." + HttpConfig.END_POINT + "/" + putObjectRequest.getObjectKey();
}


1.为了方便管理里面的并发任务,我把所有任务都扔到一个list之中,
目前能力下只能想出这个办法,里面的setdatas()方法:
是因为项目中有的图片上传需要打标记区分。
2.上传成功之后要求需要返给服务器文件名和url,当然都有不同的要求。
3.关于进度更新ui问题:我还需要测试一下上传的速率,
采用handler方法去解决。
4.突然想到到失败后重新上传的次数问题,
个人直觉是三次都失败之后才会返回失败结果,结果还是测了之后再说。


AliAuthRes代码:

public class AliAuthRes extends ResponseMsg implements BaseModel {
/**
* 鉴权key
*/
String accessKeyId;
/**
* 鉴权secret
*/
String accessKeySecret;
/**
* 鉴权token
*/
String securityToken;
/**
* 鉴权时间戳
*/
String expiration;

public String getAccessKeyId() {
return accessKeyId;
}

public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}

public String getAccessKeySecret() {
return accessKeySecret;
}

public void setAcessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}

public String getSecurityToken() {
return securityToken;
}

public void setSecurityToken(String securityToken) {
this.securityToken = securityToken;
}

public String getExpiration() {
return expiration;
}

public void setExpiration(String expiration) {
this.expiration = expiration;
}

@Override
public <T extends BaseModel> T build() {
AliAuthRes aliAuthRes=new AliAuthRes();
aliAuthRes.setSuccess(true);
return (T) aliAuthRes;
}
}


UploadListener代码:

public interface UploadListener {
/**
* 上传完成
*
* @param success
* @param failure
*/
void onUploadComplete(Map<String, Object> success, List<String> failure);
}


总结:期间走了不少弯路,事实证明真的要仔细看文档,

补充一点:上传结束后,如果有图片上传失败,用户选择不重试,记得删除已经上传成功的图片,

毕竟是垃圾文件,耗空间,而空间是要钱的。吸取教训,以后用百度云什么的就可以参考了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: