社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
目录
1. 阿里云oss账号准备
1.1 注册阿里云账号,并开通OSS存储服务
1.2 创建RAM子用户并使用子账户的AccessKeyId和AccessKeySevcret进行业务操作,主账户存在安全问题
2. 项目中集成OSS依赖
3. 重写OSSFederationCredentialProvider从自家服务获取相关参数
4. OSSClient实例全局初始化
5. 封装和集成使用工具类,完成单文件上传,单文件断点续传、多文件上传等
6. OSSTokenServer 服务搭建(这个是模拟服务端产生token的,实际开发应该交给后台完成)
7. token获取支持HTTPS服务的优化和改进
1.2 创建RAM子用户并使用子账户的AccessKeyId和AccessKeySevcret进行业务操作,主账户存在安全问题
通过管理控制后台-访问控制 https://help.aliyun.com/product/28625.html
1.2.1 RAM控制台 https://ram.console.aliyun.com/
1.2.2 STS授权相关信息获取步骤:
1.2.2.1.RAM控制台用户管理创建子用户(User)同时点击该用户创建并获取AccessKeyID和AccessKeySecret https://help.aliyun.com/document_detail/28637.html
1.2.2.2.对该子用户(User) 授予AliyunSTSAssumeRoleAccess策略(必须),如需自定义策略请看 https://help.aliyun.com/document_detail/28640.html
1.2.2.3.RAM控制台角色管理创建角色role,进行自定义授权设置(控制操作的内容),获取Arn https://help.aliyun.com/document_detail/28649.html
注意点:
2. 项目中集成OSS依赖
账号准备完成了,进入正题就是sdk的集成流程
module中添加依赖.配置完成后需同步一下
//阿里云OSS上传文件的SDK,上传内层依赖okhttp网络框架
compile 'com.aliyun.dpa:oss-android-sdk:+'
compile 'com.squareup.okhttp3:okhttp:3.11.0'
compile 'com.squareup.okio:okio:1.9.0'
proguard-rules.pro
文件配置混线代码
#阿里云文件服务sdk
-keep class com.alibaba.sdk.android.oss.** { *; }
-dontwarn okio.**
-dontwarn org.apache.commons.codec.binary.**
3. 重写OSSFederationCredentialProvider从自家服务获取相关参数
由于移动端被认为是不可信的,因而重要参数是不可以存放在移动端的,这时候需要出发自己的服务器获取 AccessKeyId,AccessKeySecret,SecurityToken,Expiration 几个重要参数
触发请求自家服务器索要授权(这里我们的思路是放在OSSAuthCredentialsProvider中进行的)
后端经返回如下代码
{
"StatusCode": "200",
"AccessKeyId": "STS.jp1jz2IHjp1jz2IH5Ef3jp1jz2IH5Ef3jp1jz2IH5Ef35Ef3",
"AccessKeySecret": "BeoXtPg1lXbtPg1lXbttPg1lXbtIqqjp1j",
"SecurityToken": "CAISkwJ1q6Ft5B22y2yyfSjIrok4uh5UUUQ7+EeP52yfSjIrokh5UytDWLIo/2yfSjIrok4uh5UdAIkq6NFHAm1D2yfSjIrok4uh5UK5yWzfg6wYvfw1NBcKq",
"Expiration": "2019-02-16T04:16:01Z"
}
OSSAuthCredentialsProvider代码如下:
package com.ztsc.house.provider;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.common.OSSConstants;
import com.alibaba.sdk.android.oss.common.auth.OSSFederationCredentialProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSFederationToken;
import com.alibaba.sdk.android.oss.common.utils.IOUtils;
import org.json.JSONObject;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Created by benchengzhou on 2019/2/15 16:25 .
* 作者邮箱: mappstore@163.com
* 功能描述: 阿里云服务上传获取上传参数表
* 类 名: OSSAuthCredentialsProvider
* 备 注:Authentication server issued under the agreement of the official website agreement, you can directly use the provider
*/
public class OSSAuthCredentialsProvider extends OSSFederationCredentialProvider {
private String mAuthServerUrl;
private com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder mDecoder;
public OSSAuthCredentialsProvider(String authServerUrl) {
this.mAuthServerUrl = authServerUrl;
}
/**
* set auth server url
*
* @param authServerUrl
*/
public void setAuthServerUrl(String authServerUrl) {
this.mAuthServerUrl = authServerUrl;
}
/**
* set response data decoder
*
* @param decoder
*/
public void setDecoder(com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder decoder) {
this.mDecoder = decoder;
}
@Override
public OSSFederationToken getFederationToken() throws ClientException {
OSSFederationToken authToken;
String authData;
try {
URL stsUrl = new URL(mAuthServerUrl);
HttpURLConnection conn = (HttpURLConnection) stsUrl.openConnection();
conn.setConnectTimeout(10000);
InputStream input = conn.getInputStream();
authData = IOUtils.readStreamAsString(input, OSSConstants.DEFAULT_CHARSET_NAME);
if (mDecoder != null) {
authData = mDecoder.decode(authData);
}
JSONObject jsonObj = new JSONObject(authData);
int statusCode = jsonObj.getInt("StatusCode");
if (statusCode == 200) {
String ak = jsonObj.getString("AccessKeyId");
String sk = jsonObj.getString("AccessKeySecret");
String token = jsonObj.getString("SecurityToken");
String expiration =/*"1000"*/ jsonObj.getString("Expiration");
authToken = new OSSFederationToken(ak, sk, token, expiration);
} else {
String errorCode = jsonObj.getString("ErrorCode");
String errorMessage = jsonObj.getString("ErrorMessage");
throw new ClientException("ErrorCode: " + errorCode + "| ErrorMessage: " + errorMessage);
}
return authToken;
} catch (Exception e) {
throw new ClientException(e);
}
}
public interface AuthDecoder {
String decode(String data);
}
}
4. OSSClient实例全局初始化
使用之前进行全局初始化,一般选择将这段代码放在
Application
中进行
/**
* 阿里对象存储服务初始化
*/
public void aliOSSInit() {
String tokenHost = "http://192.168.1.120:7080/oss/getStsToken";
//官方文档建议,OSS的初始化应该放在子线程中进行。 原因大抵是每次token过期后Oss的SDK会默认通过 OSSAuthCredentialsProvider 从我们自己的后台服务器获取token,必然要进行网络请求,这样是一个耗时的操作需要放在子线程中进行
new Thread(new Runnable() {
@Override
public void run() {
// demo 中使用这个 String endpoint = "http://img-cn-beijing.aliyuncs.com";
// String tokenHost = "http://192.168.1.120:7080";
//特别提示这个是我们自定义的类,如果仅仅需要支持http可以使用OSS默认的OSSAuthCredentialsProvider,但是如果需要自定义token下载器或者token服务是HTTPS服务,请参考OSSAuthCredentialsProvider自定义,并导包为自定义类OSSAuthCredentialsProvider,切记
OSSAuthCredentialsProvider credentialProvider = new OSSAuthCredentialsProvider(tokenHost);
// OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(ConstantValue.StsToken.AccessKeyId, ConstantValue.StsToken.SecretKeyId, ConstantValue.StsToken.SecurityToken);
// 配置类如果不设置,会有默认配置。
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒。
conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒。
conf.setMaxConcurrentRequest(5); // 最大并发请求数,默认5个。
conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次。
oss = new OSSClient(sAppContext, ConstantValue.AliOSSconfig.endpoint, credentialProvider, conf);
OSSLog.enableLog(); //调用此方法即可开启日志
}
}).run();
}
/**
* 获取Oss
*
* @return
*/
public OSS getOss() {
return oss;
}
其中
tokenHost
是后台服务提供给移动端获取 AccessKeyId,AccessKeySecret,SecurityToken,Expiration参数的服务地址,可以Android、Ios、pc通用一套
数据返回格式务必是严格的如下json形式
{
"StatusCode": "200",
"AccessKeyId": "STS.jp1jz2IHjp1jz2IH5Ef3jp1jz2IH5Ef3jp1jz2IH5Ef35Ef3",
"AccessKeySecret": "BeoXtPg1lXbtPg1lXbttPg1lXbtIqqjp1j",
"SecurityToken": "CAISkwJ1q6Ft5B22y2yyfSjIrok4uh5UUUQ7+EeP52yfSjIrokh5UytDWLIo/2yfSjIrok4uh5UdAIkq6NFHAm1D2yfSjIrok4uh5UK5yWzfg6wYvfw1NBcKq",
"Expiration": "2019-02-16T04:16:01Z"
}
5. 封装和集成使用工具类,完成单文件上传,单文件断点续传、多文件上传等
android应用开发的最该境界是组件化开发,这样既能减少代码或模块间的耦合同时也不需要每个开发的成员都去熟悉第三方SDK的API和注意事项,极大的减少了开发成本
综合上面的原因,我对阿里的OSS进行了一次封装
主要完成以下功能**,喜欢的话点我下载完整API文档**
返回值类型 | 方法名称 | 方法描述 |
---|---|---|
void | upLoadSigleFile(java.lang.String filePath, OssService.ImageUpLoadCallback imageUpLoadCallback, boolean errRetry) | 常规单个文件上传,请注意如果文件过大(相对网速考虑),请考虑使用 |
void | resumableUploadBigFile(java.lang.String filePath, OssService.ImageUpLoadCallback imageUpLoadCallback, boolean errRetry) | 单文件可断点续传操作,在网速不好或者文件比较大的情况下可以考虑使用 |
boolean | isObjectExit(java.lang.String objectKey) | 获取指定阿里oss的bucket中时候是否含有对应名称的文件对象 |
void | deleteObject(java.lang.String objectKey, OssService.DeleteObjectCallback callback) | 通过文件名删除指定地域对象存储服务器的对象,请注意不同地域相同文件名对象视为不同文件,无法同时删除 |
void | copyObjectB2B(java.lang.String srcBucketName, java.lang.String srcObjectKey, java.lang.String destBucketName, java.lang.String destObjectKey, OssService.CopyB2BCallback bCallback) | 将文件从一个存储空间(源存储空间)拷贝到同一地域的另一个存储空间(目标存储空间)中。 |
void | asyncImagesUpLoad(java.util.ArrayList<java.lang.String> imgsList, boolean onErrorRetry, OssService.ImageUpLoadCallback imageUpLoadCallback) | 多张图片一起上传服务器,并将上传后的图片路径按照对应的路径返回 |
/**
* 上传图片
*/
public void pictureUpLoad() {
ArrayList<String> imgs = new ArrayList<>();
for (int j = 0; j < localMedias.size(); j++) {
imgs.add(localMedias.get(j).getCompressPath());
}
new OssService(((MApplication) (MApplication.sAppContext)).getOss(), ConstantValue.AliOSSconfig.bucketName)
.asyncImagesUpLoad(imgs, false, new OssService.ImageUpLoadCallback() {
@Override
public void onStart() {
createLoadingDialog("正在上传");
}
@Override
public void onFinish() {
dissmissLoadingDialog();
}
@Override
public void onSuccess(ArrayList<String> fileUrlList) {
//调用在子线程UI刷新需要添加线程切换
LogUtil.e("文件上传完成");
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < fileUrlList.size(); i++) {
buffer.append(fileUrlList.get(i)).append(",");
}
commitMessage(buffer.toString()); //信息上传
}
@Override
public void onError(ClientException clientExcepion, ServiceException serviceException) {
//调用在子线程UI刷新需要添加线程切换
ToastUtils.showToastShort("上传失败,请重试");
}
@Override
public void onProgress(long currentSize, long totalSize) {
//调用在子线程UI刷新需要添加线程切换
LogUtil.e("当前进度" + currentSize + "______" + totalSize);
}
});
}
6. OSSTokenServer 服务搭建(这个是模拟服务端产生token的,实际开发应该交给后台完成)
按照下面的步骤轻松搭建 OSSTokenServer
服务
2.替换config.json中对应参数为自己家申请的参数
3.使用IDEA运行代码
4.设置对应端口
5.点击运行
Application
中的tokenHost
,运行APP即完成7. token获取支持HTTPS服务的优化和改进
上面的问题解决完成后我们发现已经完全服务我们日常的使用了,但是有时候公司获取OSS token的服务是HTTPS协议的,那这个时候就会出现问题,报错为支持的HTTPS,那这个问题曾么解决呢?
如果你知道了 OSSAuthCredentialsProvider对象其实就是OSS对Token过期下载一个管理的方式,他是当用户的token过期后,OSS主动调用 OSSAuthCredentialsProvider
的 getFederationToken
方法主动重新从我们的服务器获取TOKEN ,阿里的OSS SDK 默认为我们提供了一个 OSSAuthCredentialsProvider
类一般情况下我们直接使用这个就是可以的,没有必要自己单独去创建一个,但是特殊情况比如 希望使用自己的下载工具或者支持HTTPS获取Token等就需要使用到,自定义这个 OSSAuthCredentialsProvider
对象了。
明白了这些这个问题的解决其实很简单,我们只需要对 3 中的 OSSAuthCredentialsProvider
类文件中的代码做如下修改即可,需要强调的是 aliOSSInit
中 OSSAuthCredentialsProvider的导包一定是我们当前自定义的 OSSAuthCredentialsProvider
/**
* Created by benchengzhou on 2019/2/15 16:25 .
* 作者邮箱: mappstore@163.com
* 功能描述: 阿里云服务上传获取上传参数表
* 类 名: OSSAuthCredentialsProvider
* 备 注:Authentication server issued under the agreement of the official website agreement, you can directly use the provider
*/
public class OSSAuthCredentialsProvider extends OSSFederationCredentialProvider {
private String mAuthServerUrl;
private com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder mDecoder;
public OSSAuthCredentialsProvider(String authServerUrl) {
this.mAuthServerUrl = authServerUrl;
}
/**
* set auth server url
*
* @param authServerUrl
*/
public void setAuthServerUrl(String authServerUrl) {
this.mAuthServerUrl = authServerUrl;
}
/**
* set response data decoder
*
* @param decoder
*/
public void setDecoder(com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder decoder) {
this.mDecoder = decoder;
}
@Override
public OSSFederationToken getFederationToken() throws ClientException {
OSSFederationToken authToken;
try {
//这里可以替换自己的网络请求框架,但是注意一定要使用支持https的同步请求方法和配置
String url = BuildConfig.HOST_SERVICE_OSSTOKEN;
OkHttpClient okHttpClient = MApplicationInfo.getInstance().getSOkHttpClient();
final Request request = new Request.Builder()
.url(url)
.build();
final Call call = okHttpClient.newCall(request);
Response response = call.execute();
String data = response.body().string();
// LogUtil.e("---------------" + data);
JSONObject jsonObj = new JSONObject(data);
int statusCode = jsonObj.getInt("StatusCode");
if (statusCode == 200) {
String ak = jsonObj.getString("AccessKeyId");
String sk = jsonObj.getString("AccessKeySecret");
String token = jsonObj.getString("SecurityToken");
String expiration =/*"1000"*/ jsonObj.getString("Expiration");
authToken = new OSSFederationToken(ak, sk, token, expiration);
} else {
String errorCode = jsonObj.getString("ErrorCode");
String errorMessage = jsonObj.getString("ErrorMessage");
throw new ClientException("ErrorCode: " + errorCode + "| ErrorMessage: " + errorMessage);
}
return authToken;
} catch (Exception e) {
e.printStackTrace();
throw new ClientException(e);
}
}
public interface AuthDecoder {
String decode(String data);
}
}
注意事项
如果我们按照上面的步骤操作了但是依旧报错,请从以下两个方面排查问题 OSSAuthCredentialsProvider对象
1.OSS初始化时 OSSAuthCredentialsProvider
导包错误,应该导入自定义的
2.在主线程中进行了网络请求(这里推荐OSS初始化在子线程中进行可避免此问题)
对于OkHttp3支持HTTPS详细过程请看这篇文章 Android-OKhttp解决https安全链接请求问题
这里仅简单叙述配置过程
1,新TrustAllcert类实现X509TrustManager接口:
public class TrustAllCerts implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
}
2,方法createSSLSocketFactory()调用类TrustAllcert,获取SSLSocketFactory:
private static SSLSocketFactory createSSLSocketFactory() {
SSLSocketFactory ssfFactory = null;
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
ssfFactory = sc.getSocketFactory();
} catch (Exception e) {
}
return ssfFactory;
}
3,初始化OKHttpClient配置,注意请把这段代码放在application位置进行初始化:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.sslSocketFactory(createSSLSocketFactory());
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!