【寫在前面】
建議初次接觸OSS的童鞋可以根據(jù)本文自行操作一遍,能先實(shí)現(xiàn)功能,有時(shí)間的話再看阿里云OSS的文檔,進(jìn)行局部優(yōu)化。那是一份什么樣的文檔的呢?就是你翻來覆去的看,然后你越看就越想把寫文檔的人拉過來揍一頓的感覺,總之誰看誰知道。
閱讀本文時(shí),需要注意本文中代碼的注釋內(nèi)容;圖片模糊問題不大,可以查看原圖,而且只要能找準(zhǔn)地方就行,我都打紅框了。
【正文】
后端環(huán)境:ubuntu系統(tǒng) + python
移動(dòng)端: android
本文采用后端授權(quán)stsToken給移動(dòng)端的方式,實(shí)現(xiàn)移動(dòng)端文件直傳OSS。
【整體流程描述】
移動(dòng)端向后端發(fā)送獲取stsToken的請求,后端接收到該請求后,于后端環(huán)境中執(zhí)行請求OSS授權(quán)的stsToken的請求,然后將獲取的stsToken返回給移動(dòng)端,移動(dòng)端獲取stsToken后用以實(shí)例化OSSCredentialProvider,進(jìn)而實(shí)例化OSS,通過OSS的實(shí)例化對象實(shí)現(xiàn)上傳。
【整體流程圖示】

【實(shí)現(xiàn)過程】
一、在OSS控制臺中:
1、首先創(chuàng)建Bucket空間:
進(jìn)入OSS控制臺中找到新建Bucket,然后點(diǎn)它。就行了
2、開通sts服務(wù),如下圖,進(jìn)入你的OSS控制臺,點(diǎn)擊紅框中前往RAM控制臺

如下圖,點(diǎn)擊開始授權(quán):

如果你之前沒有創(chuàng)建過RAM賬號,則此次創(chuàng)建之后會生成AccessKeyId、AccessKeySecret這兩個(gè)密鑰和RoleArn這個(gè)角色信息,此次請務(wù)必進(jìn)行保管。如下圖會先顯示前二者:

然后會顯示第三個(gè),即角色信息RoleArn,務(wù)必保管好,如下圖:

上圖中點(diǎn)擊開始授權(quán)之后便會生成一個(gè)RAM子賬號,生成該賬號之后進(jìn)入用戶管理頁面,如下圖,紅框中即為新建的RAM賬戶:

點(diǎn)擊上圖紅框中右側(cè)的授權(quán),彈出授權(quán)框:
我看右側(cè)紅框中的那個(gè)權(quán)限可以管理整個(gè)對象存儲服務(wù),所以就加了進(jìn)來,點(diǎn)擊確定即可:

至此,RAM子賬號就創(chuàng)建完畢了。
二、后端服務(wù)器:
1、安裝pythonSDK:
在安裝SDK之前需要先安裝python-devel庫,至于原因,可以去原文檔中查找,本文只講述實(shí)現(xiàn)過程。
在Ubuntu與Debian系統(tǒng)中安裝方式:
apt-get install python-dev
若因權(quán)限導(dǎo)致無法安裝,那就加sudo
開始安裝SKD:
通過pip方式安裝
pip install oss2
驗(yàn)證安裝版本:
進(jìn)入python的交互模式進(jìn)行如下操作,
>>> import oss2
>>> oss2.__version__
如果安裝無誤則應(yīng)該返回版本號,應(yīng)該是大于2版本的。
如果整個(gè)安裝過程有任何問題,請看原文檔的解決方案,一般不會有問題。
原文檔地址:
https://help.aliyun.com/document_detail/85288.html?spm=a2c4g.11186623.6.705.75404947Sj1KeS
2、向sts服務(wù)器請求stsToken:
首先需要有sts這個(gè)庫,只要一步安裝操作:
pip install aliyun-python-sdk-sts
下面就是在python中請求stsToken:
def getToken():
# Endpoint以杭州為例,其它Region請按實(shí)際情況填寫。
endpoint = 'oss-cn-hangzhou.aliyuncs.com'
# 阿里云主賬號AccessKey擁有所有API的訪問權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號進(jìn)行API訪問或日常運(yùn)維,請登錄 https://ram.console.aliyun.com 創(chuàng)建RAM賬號。
access_key_id = 'LTAIq*******' #替換成你的
access_key_secret = 'wVlXxgdW*******' #替換成你的
bucket_name = '你的存儲空間名稱' #替換成你的
# role_arn是角色的資源名稱。
role_arn = 'acs:ram::16484498*******eratorrole' #替換成你的
clt = client.AcsClient(access_key_id, access_key_secret, 'cn-hangzhou')
req = AssumeRoleRequest.AssumeRoleRequest()
# 設(shè)置返回值格式為JSON。
req.set_accept_format('json')
req.set_RoleArn(role_arn)
req.set_RoleSessionName('session-name')
body = clt.do_action(req)
# 使用RAM賬號的AccessKeyId和AccessKeySecret向STS申請臨時(shí)token,這個(gè)token將會傳給移動(dòng)端使用。
stsToken = json.loads(body.decode())
return stsToken
我返回給前端這個(gè)stsToken數(shù)據(jù)結(jié)構(gòu)如下圖所示:

移動(dòng)端就是要紅框中的四個(gè)字段,這四個(gè)字段必須要返回給移動(dòng)端保存,也不必管這個(gè)四個(gè)字段是干什么的。
至此,后端請求stsToken并將其返回給移動(dòng)端的授權(quán)過程結(jié)束。
三、移動(dòng)端(本文中為Android):
1、首先在gradle的dependencies中添加oss的依賴:
// 阿里云oss
implementation 'com.aliyun.dpa:oss-android-sdk:+'
原文檔中說在Maven項(xiàng)目中加入依賴,當(dāng)時(shí)感覺明顯是在gradle,難道是我孤陋寡聞了?就問了一下阿里那邊,最后表示就是gradle。
2、添加權(quán)限:
注意安卓6.0以后需要使用動(dòng)態(tài)權(quán)限,自行處理吧
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
3、添加混淆:
在proguard-rules.pro文件中添加:
-keep class com.alibaba.sdk.android.oss.** { *; }
-dontwarn okio.**
-dontwarn org.apache.commons.codec.binary.**
然后注意設(shè)置:
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
4、然后就是在代碼中使用:
//請求后端返回StsToken
private void getSts(){
basicsNetWorkAPI = HttpClient.create(BasicsNetWorkAPI.class);
//請求后端返回stsToken的接口,每個(gè)人的網(wǎng)絡(luò)請求也許不一樣,因此,獲取的stsToken結(jié)果,請各自保存
Call<StsServerBean> call = basicsNetWorkAPI.getStsToken();
call.enqueue(new Callback<StsServerBean>() {
@Override
public void onResponse(retrofit2.Call<StsServerBean> call, Response<StsServerBean> response) {
//上文中stsToken數(shù)據(jù)結(jié)構(gòu)圖中的紅框中的四個(gè)字段內(nèi)容被我封裝成一個(gè)Bean,為方便理解取名為stsTokenBean
//為簡化理解,后文中所述stsToken為由該四個(gè)字段所組成,而不是前文的stsToken數(shù)據(jù)結(jié)構(gòu)圖的那一大坨json,因?yàn)槠渌侄斡貌坏? StsTokenBean stsTokenBean = response.body().getCredentials();
//請求成功后,獲取到那四個(gè)字段并保存到StsTokenBean后,應(yīng)該調(diào)用上傳文件的方法向OSS進(jìn)行上傳。
//事實(shí)上,此處的StsTokenBean應(yīng)該保存到緩存,因?yàn)橐苿?dòng)端并不是每次上傳文件到OSS都需要請求stsToken的
//因?yàn)槟菢泳蜁苈闊虼?,請求一次之后就會存在一個(gè)有效期,在有效期內(nèi)可以直接再次對OSS進(jìn)行操作,而上述四個(gè)參數(shù)中的Expiration即用來描述有效期的
//你可以自行設(shè)置,在超過有效期后進(jìn)行重新獲取stsToken的操作,當(dāng)然若時(shí)間緊迫,你也可以按照本文的方式首先實(shí)現(xiàn)功能即可。后續(xù)改進(jìn)
//上傳文件
upload_file(stsTokenBean);
}
@Override
public void onFailure(retrofit2.Call<StsServerBean> call, Throwable t) {
}
});
}
//上傳文件方法
private void upload_file(StsTokenBean stsTokenBean){
//根據(jù)你的OSS的地區(qū)而自行定義,本文中的是杭州
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
//移動(dòng)端建議使用該方式,此時(shí),stsToken中的前三個(gè)參數(shù)就派上用場了
OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(stsTokenBean.getAccessKeyId(), stsTokenBean.getAccessKeySecret(), stsTokenBean.getSecurityToken());
// 配置類如果不設(shè)置,會有默認(rèn)配置。
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(15 * 1000); // 連接超時(shí),默認(rèn)15秒。
conf.setSocketTimeout(15 * 1000); // socket超時(shí),默認(rèn)15秒。
conf.setMaxConcurrentRequest(5); // 最大并發(fā)請求數(shù),默認(rèn)5個(gè)。
conf.setMaxErrorRetry(2); // 失敗后最大重試次數(shù),默認(rèn)2次。
//初始化OSS服務(wù)的客戶端oss
//事實(shí)上,初始化OSS的實(shí)例對象,應(yīng)該具有與整個(gè)應(yīng)用程序相同的生命周期,在應(yīng)用程序生命周期結(jié)束時(shí)銷毀
//但這里只是實(shí)現(xiàn)功能,若時(shí)間緊,你仍然可以按照本文方式先將功能實(shí)現(xiàn),然后優(yōu)化
OSS oss = new OSSClient(getActivity().getApplicationContext(), endpoint, credentialProvider, conf);
//當(dāng)前時(shí)間戳,用于自定義文件在OSS中存儲路徑末尾的名稱
image_url_time = System.currentTimeMillis() + "";
// 構(gòu)造上傳請求,第二個(gè)數(shù)參是ObjectName,第三個(gè)參數(shù)是本地文件路徑
PutObjectRequest put = new PutObjectRequest("first-images", image_url_time, loacalFilePath);
//異步上傳可以設(shè)置進(jìn)度回調(diào)
put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
@Override
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
Log.i("上傳進(jìn)度:", "當(dāng)前進(jìn)度" + currentSize + " 總進(jìn)度" + totalSize);
}
});
//實(shí)現(xiàn)異步上傳
OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
Log.d("PutObject", "UploadSuccess");
Log.d("ETag", result.getETag());
Log.d("RequestId", result.getRequestId());
//這個(gè)image_url左邊的字符串部分是我OSS的Bucket的文件存儲地址,根據(jù)個(gè)人的文件存儲地址不同,替換成自己的即可,而后面的image_url_time則是為了區(qū)分每個(gè)文件的文件名
//注意,最好的方式是設(shè)置回調(diào),因?yàn)榛卣{(diào)的功能必須要在線上服務(wù)器才能測試,我服務(wù)器在本地環(huán)境中是不允許回調(diào)的
//在咨詢阿里云相關(guān)人員之后,他們說也允許記住地址,進(jìn)行拼接的方式保存線上文件url路徑
//但是這種方式需要在OSS的管理控制臺中將你的存儲空間設(shè)置為公共讀的方式,不然沒法用下面的拼接鏈接。
//此時(shí)你上傳的文件所在的線上地址就已經(jīng)獲得了,想怎么使用則隨意了
image_url = "http://first********ngzhou.aliyuncs.com/" + image_url_time;
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
if (clientException != null) {
// 本地異常,如網(wǎng)絡(luò)異常等。
clientException.printStackTrace();
}
if (serviceException != null) {
// 服務(wù)異常。
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
}
}
});
// 等異步上傳過程完成
task.waitUntilFinished();
Toast.makeText(getActivity(), "上傳成功", Toast.LENGTH_SHORT).show();
至此,移動(dòng)端的直接上傳文件到OSS的過程也結(jié)束了。你可以去自己的OSS控制臺看看是否新增了剛才上傳的文件。
整個(gè)OSS的集成過程完成!