如今客户端app开发中 涉及到上传文件之类的功能大多数都是直接上传到第三方服务器 而自己应用服务器中只保留逻辑代码不建议保留文件 本文我将介绍android客户端直接上传到阿里云服务器(OSS)流程 以及关键代码
其实上传到oss和上传到应用服务器方式大同小异 只是前提是必须配置OSS的一些参数 用OSS官方文档提供的上传请求对象来实现上传
首先第一步 在项目中依赖OSS服务器SDK 也可以以添加jar包方式
dependencies {
compile 'com.aliyun.dpa:oss-android-sdk:2.4.5'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okio:okio:1.9.0'
}
添加需要的权限 如果有则不需要重复添加
重新编译项目 如果没有报错便是集成成功
这个时候我们其实可以用SKD中一些类 和方法 来写请求上传的代码了
但是在这之前 还有一步很重要的操作 就是和oss认证 如果没有认证的话oss是不允许访问的 换句话来说 如果服务器都不知道你是谁 怎么可能让你随便访问它
而OSS认证方式有2种
1:STS鉴权模式(官方推荐)
2:自签名模式
第一种STS方式 是官方推荐的 安全度也很高 所以本文我采用这种方式来认证
简单来说这种方式 就是每次连接OSS之前 需要获取一些配置参数 然后用这些参数来初始化OSS SKD 才能进行上传 而这个初始化就相当于认证
最关键的三个参数
AccessKeyId
SecretKeyId
SecurityToken
其中AccessKeyId和SecretKeyId是从阿里云服务器直接申请的 SecurityToken类似于一个标识 重要的是这个参数是有时效性的 也就是说会过期的
那么这三个参数 我这里是从应用服务器 和后台对接好 然后后台帮我获取的
大概流程是 我每次要上传文件之前 先去自己应用服务器 获取这三个参数
而应用服务器负责从阿里云oss服务器 获取 在给我返回
返回的json格式如下
其中的Expiration字段是过期时间 暂时不理会
这三个参数拿到了之后我们就开始初始化oss SDK
比如某个页面要有上传文件功能
那么进入这个页面开始 我们首先就配置
一般在onStart方法中执行就可以
private void getOssConfig(){
OkHttpUtils.ResultCallback loadNewsCallback = new OkHttpUtils.ResultCallback() {
@Override
public void onSuccess(OssConfigBean response) {
OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(response.getAccessKeyId(), response.getAccessKeySecret(), response.getSecurityToken());
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒
conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒
conf.setMaxConcurrentRequest(8); // 最大并发请求数,默认5个
conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次
oss = new OSSClient(getApplicationContext(), Urls.OSSENDOPINT, credentialProvider,conf);
}
@Override
public void onFailure(Exception e) {
}
};
OkHttpUtils.get(Urls.GETOSSDATACONFIG,loadNewsCallback);
}
代码很简单 通过自己封装的oKHttp执行一个get请求 在请求成功的方法中来获取三个参数
其中OssConfigBean是一个存放三个参数信息的Bean 因为返回的是json
封装的okHttp中直接写好了 Gson解析的代码 所以泛型直接传入该Bean就好
OssConfigBean如下:
接下来创建OSSCredentialProvider 对象 构造中直接配置获取的三个参数
创建ClientConfiguration 对象配来配置一些设置 如果不配置就是默认设置
最后创建全局对象
oss = new OSSClient(getApplicationContext(), Urls.OSSENDOPINT, credentialProvider,conf);
其中有三个参数 第一个参数是上下文对象 不必多说了
第二个是oss服务器的地址域名 是一个字符串
比如
http://oss-cn-beijing.aliyuncs.com
这个域名地址 创建oss服务器的时候就应该是有的
第三个参数 就是我们刚才创建的OSSCredentialProvider 对象
第四个参数是一些配置信息 如果不要自定义配置 则不传
到这里 其实如果参数没有获取错误的话 就初始化成功了
其实我们最终就是得到了一个oss对象 因为接下来我们要用这个对象来发起请求 所以都是步步相关 不难理解
接下来我们开始写上传文件代码
首先创建一个PutObjectRequest对象
PutObjectRequest put = new PutObjectRequest(Urls.OSSBUCKET, OssPath,path);
这里需要三个参数
第一个参数是BUCKETNAME
这里要说一下这个BUCKETNAME 是什么东西
我们通过app客户端直接上传到oss 并没有指定我们要传到服务器中哪个位置
而BUCKET我们就可以理解为是一个仓库 BUCKETNAME 就是仓库名字
第一个参数需要的就是这个
我们上传的文件 如果指定了是这个仓库名字 那么都会传入到这里
下面是oss客户端 给大家看一下方便理解
很显然我这里的名字就是haoyuehuyu 所以我第一个参数填写的就是这个
这个仓库在创建oss服务器的管理员可以创建
接下来看第二个参数
这个参数是objectKey
那么什么是objectKey呢?
其实就是一个自己编写的字符串路径 意思就是你想把要上传的文件上传到仓库中哪个位置 因为仓库下面肯定要分好多文件夹的
比如我这里写的就是
test/auth/当前年月日/时分秒毫秒.jpg
所以如果上传成功了
我们在仓库中按照文件夹路径就可以看到这个文件
注意:这个路径一定要指定到文件级别
接下来看第三个参数
uploadFilePath
实际上要的就是一个本地文件的路径
注意:这里要的不是一个file文件 是一个本地图片路径
创建成功请求对象之后
我们可以选择异步上传和同步上传
相信这里也不需要选择了 因为android根本不建议同步执行网络任务
所以来写异步
通过刚才创建的 PutObjectRequest对象可以设置一个异步进度回调
// 异步上传时可以设置进度回调
put.setProgressCallback(new OSSProgressCallback() {
@Override
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
Log.d("PutObject", "当前大小: " + currentSize + " 总大小: " + totalSize);
}
});
OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback() {
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
Log.d("PutObject", "UploadSuccess");
Log.d("ETag", result.getETag());
Log.d("RequestId", result.getRequestId());
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
// 请求异常
if (clientExcepion != null) {
// 本地异常如网络异常等
clientExcepion.printStackTrace();
}
if (serviceException != null) {
// 服务异常
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
}
}
});
这里通过最开始初始化时候创建好的oss对象来执行异步上传文件
很显然 分为成功和失败回调
但是如果仔细看 实际上是一个task任务
所以我们可以根据状态
// task.cancel(); // 可以取消任务
// task.waitUntilFinished(); // 可以等待直到任务完成
其实到这里上传到oss的全部内容已经写完了 过程当中我遇到一个问题
就是配置参数 都配置好了 代码写的也没问题 但是上传的时候执行上传失败的方法 打印log :Access denied by authorizer policy.
意思为是访问被授权人政策拒绝。
也就是说我们客户端没问题 但是oss服务器认为我们还是没权限访问 而这个权限策略 是oss管理员来编写的 所以要和后台人员协调好
如果遇到问题了可以去这里排查一下具体是什么问题
https://www.alibabacloud.com/help/zh/doc-detail/42777.htm
最后把上传全部代码贴上 这里我以上传多图举例:
首先在初始化页面的时候 初始化oss 服务器sdk
private void getOssConfig(){
OkHttpUtils.ResultCallback loadNewsCallback = new OkHttpUtils.ResultCallback() {
@Override
public void onSuccess(OssConfigBean response) {
OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(response.getAccessKeyId(), response.getAccessKeySecret(), response.getSecurityToken());
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒
conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒
conf.setMaxConcurrentRequest(8); // 最大并发请求数,默认5个
conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次
oss = new OSSClient(getApplicationContext(), Urls.OSSENDOPINT, credentialProvider,conf);
}
@Override
public void onFailure(Exception e) {
}
};
OkHttpUtils.get(Urls.GETOSSDATACONFIG,loadNewsCallback);
}
private void postToOss(String path) {
Log.i(TAG,"size:"+paths.size()+"-"+num+"--"+path);
num++;
if(progress==null){
progress = CustomProgress.show(this, "正在上传", false, null);
}
String[] photoFileName = SynthesisUtils.getPhotoFileName();
String OssPath = "test/auth/"+photoFileName[0]+"/"+photoFileName[1];
PutObjectRequest put = new PutObjectRequest(Urls.OSSBUCKET, OssPath,path);
// 异步上传时可以设置进度回调
put.setProgressCallback(new OSSProgressCallback() {
@Override
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
if(currentSize==totalSize){
progress.dismiss();
}
Log.d("PutObject", "当前大小: " + currentSize + " 总大小: " + totalSize);
}
});
OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback() {
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
Log.d("PutObject", "UploadSuccess");
Log.d("ETag", result.getETag());
Log.d("RequestId", result.getRequestId());
// 这里进行递归单张图片上传
if (num <= paths.size() - 1) {
posyToOss(paths.get(num));
}else{
//这里是出口
}
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
// 请求异常
if (clientExcepion != null) {
// 本地异常如网络异常等
clientExcepion.printStackTrace();
}
if (serviceException != null) {
// 服务异常
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
}
}
});
}
这里的progress是我自己封装的上传时候的一个等待弹框
SynthesisUtils.getPhotoFileName()这个方法中我会返回一个数组
数组【0】是当前年月日的字符串 比如20180125
数组【1】是当前时分秒毫秒字符串
也就是说数组0下标返回的是 文件夹名字 以当前年月日命名
数组【1】返回的是文件名字
所以最终的路径也就是 test/auth/年月日/时分秒+文件后缀名
也就是说我们要把这个文件传入到这个路径
然后paths集合中我存放的是一选择的图片路径
这里采用递归方式调用
代码不难 最重要的是配置那些参数信息要搞懂 希望对大家有帮助 如果有问题可以留言