【写在前面】
建议初次接触OSS的童鞋可以根据本文自行操作一遍,能先实现功能,有时间的话再看阿里云OSS的文档,进行局部优化。那是一份什么样的文档的呢?就是你翻来覆去的看,然后你越看就越想把写文档的人拉过来揍一顿的感觉,总之谁看谁知道。
阅读本文时,需要注意本文中代码的注释内容;图片模糊问题不大,可以查看原图,而且只要能找准地方就行,我都打红框了。
【正文】
后端环境:ubuntu系统 + python
移动端: android
本文采用后端授权stsToken给移动端的方式,实现移动端文件直传OSS。
【整体流程描述】
移动端向后端发送获取stsToken的请求,后端接收到该请求后,于后端环境中执行请求OSS授权的stsToken的请求,然后将获取的stsToken返回给移动端,移动端获取stsToken后用以实例化OSSCredentialProvider,进而实例化OSS,通过OSS的实例化对象实现上传。
【整体流程图示】
【实现过程】
一、在OSS控制台中:
1、首先创建Bucket空间:
进入OSS控制台中找到新建Bucket,然后点它。就行了
2、开通sts服务,如下图,进入你的OSS控制台,点击红框中前往RAM控制台
如下图,点击开始授权:
如果你之前没有创建过RAM账号,则此次创建之后会生成AccessKeyId、AccessKeySecret这两个密钥和RoleArn这个角色信息,此次请务必进行保管。如下图会先显示前二者:
然后会显示第三个,即角色信息RoleArn,务必保管好,如下图:
上图中点击开始授权之后便会生成一个RAM子账号,生成该账号之后进入用户管理页面,如下图,红框中即为新建的RAM账户:
点击上图红框中右侧的授权,弹出授权框:
我看右侧红框中的那个权限可以管理整个对象存储服务,所以就加了进来,点击确定即可:
至此,RAM子账号就创建完毕了。
二、后端服务器:
1、安装pythonSDK:
在安装SDK之前需要先安装python-devel库,至于原因,可以去原文档中查找,本文只讲述实现过程。
在Ubuntu与Debian系统中安装方式:
apt-get install python-dev
若因权限导致无法安装,那就加sudo
开始安装SKD:
通过pip方式安装
pip install oss2
验证安装版本:
进入python的交互模式进行如下操作,
>>> import oss2
>>> oss2.__version__
如果安装无误则应该返回版本号,应该是大于2版本的。
如果整个安装过程有任何问题,请看原文档的解决方案,一般不会有问题。
原文档地址:
https://help.aliyun.com/document_detail/85288.html?spm=a2c4g.11186623.6.705.75404947Sj1KeS
2、向sts服务器请求stsToken:
首先需要有sts这个库,只要一步安装操作:
pip install aliyun-python-sdk-sts
下面就是在python中请求stsToken:
def getToken():
# Endpoint以杭州为例,其它Region请按实际情况填写。
endpoint = 'oss-cn-hangzhou.aliyuncs.com'
# 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建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()
# 设置返回值格式为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申请临时token,这个token将会传给移动端使用。
stsToken = json.loads(body.decode())
return stsToken
我返回给前端这个stsToken数据结构如下图所示:
移动端就是要红框中的四个字段,这四个字段必须要返回给移动端保存,也不必管这个四个字段是干什么的。
至此,后端请求stsToken并将其返回给移动端的授权过程结束。
三、移动端(本文中为Android):
1、首先在gradle的dependencies中添加oss的依赖:
// 阿里云oss
implementation 'com.aliyun.dpa:oss-android-sdk:+'
原文档中说在Maven项目中加入依赖,当时感觉明显是在gradle,难道是我孤陋寡闻了?就问了一下阿里那边,最后表示就是gradle。
2、添加权限:
注意安卓6.0以后需要使用动态权限,自行处理吧
<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.**
然后注意设置:
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
4、然后就是在代码中使用:
//请求后端返回StsToken
private void getSts(){
basicsNetWorkAPI = HttpClient.create(BasicsNetWorkAPI.class);
//请求后端返回stsToken的接口,每个人的网络请求也许不一样,因此,获取的stsToken结果,请各自保存
Call<StsServerBean> call = basicsNetWorkAPI.getStsToken();
call.enqueue(new Callback<StsServerBean>() {
@Override
public void onResponse(retrofit2.Call<StsServerBean> call, Response<StsServerBean> response) {
//上文中stsToken数据结构图中的红框中的四个字段内容被我封装成一个Bean,为方便理解取名为stsTokenBean
//为简化理解,后文中所述stsToken为由该四个字段所组成,而不是前文的stsToken数据结构图的那一大坨json,因为其他字段用不到
StsTokenBean stsTokenBean = response.body().getCredentials();
//请求成功后,获取到那四个字段并保存到StsTokenBean后,应该调用上传文件的方法向OSS进行上传。
//事实上,此处的StsTokenBean应该保存到缓存,因为移动端并不是每次上传文件到OSS都需要请求stsToken的
//因为那样就会很麻烦,因次,请求一次之后就会存在一个有效期,在有效期内可以直接再次对OSS进行操作,而上述四个参数中的Expiration即用来描述有效期的
//你可以自行设置,在超过有效期后进行重新获取stsToken的操作,当然若时间紧迫,你也可以按照本文的方式首先实现功能即可。后续改进
//上传文件
upload_file(stsTokenBean);
}
@Override
public void onFailure(retrofit2.Call<StsServerBean> call, Throwable t) {
}
});
}
//上传文件方法
private void upload_file(StsTokenBean stsTokenBean){
//根据你的OSS的地区而自行定义,本文中的是杭州
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
//移动端建议使用该方式,此时,stsToken中的前三个参数就派上用场了
OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(stsTokenBean.getAccessKeyId(), stsTokenBean.getAccessKeySecret(), stsTokenBean.getSecurityToken());
// 配置类如果不设置,会有默认配置。
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒。
conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒。
conf.setMaxConcurrentRequest(5); // 最大并发请求数,默认5个。
conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次。
//初始化OSS服务的客户端oss
//事实上,初始化OSS的实例对象,应该具有与整个应用程序相同的生命周期,在应用程序生命周期结束时销毁
//但这里只是实现功能,若时间紧,你仍然可以按照本文方式先将功能实现,然后优化
OSS oss = new OSSClient(getActivity().getApplicationContext(), endpoint, credentialProvider, conf);
//当前时间戳,用于自定义文件在OSS中存储路径末尾的名称
image_url_time = System.currentTimeMillis() + "";
// 构造上传请求,第二个数参是ObjectName,第三个参数是本地文件路径
PutObjectRequest put = new PutObjectRequest("first-images", image_url_time, loacalFilePath);
//异步上传可以设置进度回调
put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
@Override
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
Log.i("上传进度:", "当前进度" + currentSize + " 总进度" + totalSize);
}
});
//实现异步上传
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());
//这个image_url左边的字符串部分是我OSS的Bucket的文件存储地址,根据个人的文件存储地址不同,替换成自己的即可,而后面的image_url_time则是为了区分每个文件的文件名
//注意,最好的方式是设置回调,因为回调的功能必须要在线上服务器才能测试,我服务器在本地环境中是不允许回调的
//在咨询阿里云相关人员之后,他们说也允许记住地址,进行拼接的方式保存线上文件url路径
//但是这种方式需要在OSS的管理控制台中将你的存储空间设置为公共读的方式,不然没法用下面的拼接链接。
//此时你上传的文件所在的线上地址就已经获得了,想怎么使用则随意了
image_url = "http://first********ngzhou.aliyuncs.com/" + image_url_time;
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
if (clientException != null) {
// 本地异常,如网络异常等。
clientException.printStackTrace();
}
if (serviceException != null) {
// 服务异常。
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();
至此,移动端的直接上传文件到OSS的过程也结束了。你可以去自己的OSS控制台看看是否新增了刚才上传的文件。
整个OSS的集成过程完成!