**關於阿里雲oss圖片批量上傳問題解析**
阿新 • • 發佈:2019-02-15
關於阿里雲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.getAccessKeyId(), 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);
}
總結:期間走了不少彎路,事實證明真的要仔細看文件,
補充一點:上傳結束後,如果有圖片上傳失敗,使用者選擇不重試,記得刪除已經上傳成功的圖片,
畢竟是垃圾檔案,耗空間,而空間是要錢的。吸取教訓,以後用百度雲什麼的就可以參考了。