android客户端直传文件到阿里云OSS服务器详细流程代码

如今客户端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'
    }

添加需要的权限 如果有则不需要重复添加

   <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>

重新编译项目 如果没有报错便是集成成功
这个时候我们其实可以用SKD中一些类 和方法 来写请求上传的代码了
但是在这之前 还有一步很重要的操作 就是和oss认证 如果没有认证的话oss是不允许访问的 换句话来说 如果服务器都不知道你是谁 怎么可能让你随便访问它

而OSS认证方式有2种
1:STS鉴权模式(官方推荐)
2:自签名模式
第一种STS方式 是官方推荐的 安全度也很高 所以本文我采用这种方式来认证
简单来说这种方式 就是每次连接OSS之前 需要获取一些配置参数 然后用这些参数来初始化OSS SKD 才能进行上传 而这个初始化就相当于认证
最关键的三个参数
AccessKeyId
SecretKeyId
SecurityToken
其中AccessKeyId和SecretKeyId是从阿里云服务器直接申请的 SecurityToken类似于一个标识 重要的是这个参数是有时效性的 也就是说会过期的
那么这三个参数 我这里是从应用服务器 和后台对接好 然后后台帮我获取的
大概流程是 我每次要上传文件之前 先去自己应用服务器 获取这三个参数
而应用服务器负责从阿里云oss服务器 获取 在给我返回

返回的json格式如下


图片.png

其中的Expiration字段是过期时间 暂时不理会

这三个参数拿到了之后我们就开始初始化oss SDK

比如某个页面要有上传文件功能
那么进入这个页面开始 我们首先就配置
一般在onStart方法中执行就可以

 private void getOssConfig(){
        OkHttpUtils.ResultCallback<OssConfigBean> loadNewsCallback = new OkHttpUtils.ResultCallback<OssConfigBean>() {
            @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如下:


图片.png

接下来创建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客户端 给大家看一下方便理解


图片.png

很显然我这里的名字就是haoyuehuyu 所以我第一个参数填写的就是这个
这个仓库在创建oss服务器的管理员可以创建

接下来看第二个参数
这个参数是objectKey
那么什么是objectKey呢?
其实就是一个自己编写的字符串路径 意思就是你想把要上传的文件上传到仓库中哪个位置 因为仓库下面肯定要分好多文件夹的
比如我这里写的就是
test/auth/当前年月日/时分秒毫秒.jpg
所以如果上传成功了
我们在仓库中按照文件夹路径就可以看到这个文件


图片.png

注意:这个路径一定要指定到文件级别

接下来看第三个参数
uploadFilePath
实际上要的就是一个本地文件的路径
注意:这里要的不是一个file文件 是一个本地图片路径

创建成功请求对象之后
我们可以选择异步上传和同步上传
相信这里也不需要选择了 因为android根本不建议同步执行网络任务
所以来写异步
通过刚才创建的 PutObjectRequest对象可以设置一个异步进度回调

// 异步上传时可以设置进度回调
        put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
            @Override
            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
     
                Log.d("PutObject", "当前大小: " + 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());
           
            }
            @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<OssConfigBean> loadNewsCallback = new OkHttpUtils.ResultCallback<OssConfigBean>() {
         @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<PutObjectRequest>() {
         @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<PutObjectRequest, PutObjectResult>() {
         @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集合中我存放的是一选择的图片路径 
这里采用递归方式调用 

代码不难 最重要的是配置那些参数信息要搞懂 希望对大家有帮助 如果有问题可以留言 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,650评论 18 139
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,951评论 6 13
  • CorelDRAW 我想对非专业平面设计人士说,学一下CorelDRAW吧,他比PS(Photoshop)更实用,...
    谢兰英阅读 2,704评论 0 5
  • 最近总是这样,唉声叹气。 看了一本书,起起伏伏,跌跌宕宕,最后作者说是做了一个梦。 哎。 一点小事,说了不在乎,心...
    喜欢鱼的阿荣阅读 235评论 0 1