全部产品
存储与CDN 数据库 安全 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网
对象存储 OSS

快速搭建移动应用直传服务

更新时间:2017-11-15 10:53:21

背景

在移动互联的时代,手机APP上传的数据越来越多,我们可以把数据存储的问题交给OSS,让开发者更加专注于自己的应用逻辑。

本文主要介绍如何在30分钟内搭建一个基于OSS的移动应用数据直传服务。所谓直传就是移动应用数据的上传和下载直接连接OSS,只有控制流走用户自己的服务器。

优势

搭建一个基于OSS的移动应用数据直传服务,具有以下优势:

  • 上传下载方式更加安全(临时、灵活的赋权鉴权)。
  • 成本低,用户不需要准备很多服务器。移动应用直联云存储,只有控制流走用户自己的应用服务器。
  • 高并发,支持海量用户(OSS有海量的上传和下载带宽)。
  • 弹性(OSS有无限扩容的存储空间)。
  • 方便,可以方便的对接到媒体转码服务-视频多端适配,图片处理服务,CDN加速下载等。

架构图如下所示:

详细请参考这里

解析

  • Android/iOS 移动应用,即最终用户手机上的APP。
  • OSS,即阿里云对象存储,负责存储APP上传的数据,可以参考官网介绍
  • RAM/STS负责生成临时上传凭证。
  • 用户应用服务器,即提供该Android/iOS应用的开发者开发的APP后台服务,管理APP上传和下载的Token,以及用户在APP上传数据的元数据信息。

实现步骤

  1. 应用向用户的应用服务器申请一个临时上传凭证。

    Android/iOS应用不能直接存储AccessKeyID/AccessKeySecret,这样会存在泄密的风险。所以应用必须向用户的应用服务器申请一个临时上传凭证(下文将此临时上传凭证称为Token)。这个Token是有时效性的,如果这个Token的过期时间是30分钟(这个时间可以由应用服务器指定),那么在这30分钟里面,该Android/iOS应用可以使用这个Token从OSS上传和下载数据, 30分钟后再重新获取。

  2. 用户的应用服务器检测上述请求的合法性,然后返回Token给应用。

  3. 手机拿到这个Token后就可以将数据上传到OSS,或者从OSS下载数据了。

本文档主要介绍下图中红色和蓝色框的内容。

  • 蓝色框:应用服务器如何生成这个Token
  • 红色框:Android/iOS应用如何获取Token

实现效果

如下图所示,您可以扫描二维码,安装示例APP程序。此工具是用Android开发的,本文档的应用服务器也适用于iOS。

示例应用的最终效果图如下:

注意:应用服务器中的地址是示例地址。您可以参考文章结尾的STS应用服务器代码,自己部署应用服务器。

  • 应用服务器:该移动应用对应的后台应用服务器。

  • 上传Bucket:该移动应用要把数据上传到哪个Bucket。

  • 区域上传Bucket对应的区域。

示例APP的使用步骤:

  • 单击选择图片,然后把文件上传到OSS。

  • 上传的方法支持普通上传和断点上传。

    注意:在一些网络环境差的情况下,最好用断点上传。可以利用图片处理服务,对将要上传的图片进行缩略和加水印处理。初始使用请暂时先不要更改应用服务器地址和Bucket名字。

搭建直传服务前提条件

搭建直传服务需要完成以下准备工作:

  1. 开通OSS,并且创建Bucket

  2. 开通STS服务。

    1. 登录 OSS管理控制台

    2. 在OSS概览页中找到基础配置区域,单击 安全令牌,如下图所示:

      安全令牌

    3. 进入到 安全令牌快捷配置 页面。

      注意:如果没有开通RAM,会弹出开通的对话框。直接单击 开通,并进行实名验证。做完后跳到本页面。单击 开始授权

    4. 系进行自动授权,请务必保存下图中三个红框内的参数。单击保存AK信息后,对话框会关闭,STS的开通完成。


    5. 如果您之前已经创建了AccessKeyId/AccessKeySecret, 打开的页面如下:

      1. 您可以单击如下图所示的查看

      2. 单击如下图所示的创建AccessKey

      3. 记下如下参数1、2、3。

      4. 保存这三个参数后,STS的开通已经完成了。

搭建一个应用服务器

应用服务器示例的配置

注意:这个例子是采用PHP编写的,用户也可以选择自己喜欢的语言进行编写,如Java 、Python、 Go、Ruby、Node.js、C#等。

为了方便开发,本教程提供了多个语言的版本示例程序供您下载,下载地址见文章最底部。

每个语言包下载下来后,都会有一个配置文件config.json如下所示:

  1. {
  2. "AccessKeyID" : "",
  3. "AccessKeySecret" : "",
  4. "RoleArn" : "",
  5. "TokenExpireTime" : "900",
  6. "PolicyFile": "policy/all_policy.txt"
  7. }

说明:

  1. AccessKeyID:填写上述图标红的参数1的内容。
  2. AccessKeySecret:填写上述图标红的参数2的内容。
  3. RoleArn:填写上述图标红的参数3的内容。
  4. TokenExpireTime:指Android/iOS应用取到这个Token的失效时间,注意,最少是900s,默认值可以不修改。
  5. PolicyFile:填写的是该Token所要拥有的权限列表的文件, 默认值可以不修改。

本文档准备了三种最常用token权限文件,放于policy目录下面。分别是:

  • all_policy.txt:指定了该token拥有对该账号下创建Bucket、删除Bucket、上传文件、下载文件、删除文件的权限 。
  • bucket_read_policy.txt:指定了该token拥有该账号下对指定Bucket的读权限。
  • bucket_read_write_policy.txt:指定了该token拥有该账号下对指定Bucket的读写权限。

如果您想要指定这个Token只能对指定的bucket有读写权限, 请把(bucket_read_policy.txt、 bucket_read_write_policy.txt)这些文件里面$BUCKET_NAME直接替换成指定的bucket名字。

返回的数据格式解析:

  1. //正确返回
  2. {
  3. "StatusCode":200,
  4. "AccessKeyId":"STS.3p***dgagdasdg",
  5. "AccessKeySecret":"rpnwO9***tGdrddgsR2YrTtI",
  6. "SecurityToken":"CAES+wMIARKAAZhjH0EUOIhJMQBMjRywXq7MQ/cjLYg80Aho1ek0Jm63XMhr9Oc5s˙∂˙∂3qaPer8p1YaX1NTDiCFZWFkvlHf1pQhuxfKBc+mRR9KAbHUefqH+rdjZqjTF7p2m1wJXP8S6k+G2MpHrUe6TYBkJ43GhhTVFMuM3BZajY3VjZWOXBIODRIR1FKZjIiEjMzMzE0MjY0NzM5MTE4NjkxMSoLY2xpZGSSDgSDGAGESGTETqOio6c2RrLWRlbW8vKgoUYWNzOm9zczoqOio6c2RrLWRlbW9KEDExNDg5MzAxMDcyNDY4MThSBTI2ODQyWg9Bc3N1bWVkUm9sZVVzZXJgAGoSMzMzMTQyNjQ3MzkxMTg2OTExcglzZGstZGVtbzI=",
  7. "Expiration":"2015-12-12T07:49:09Z",
  8. }
  9. //错误返回
  10. {
  11. "StatusCode":500,
  12. "ErrorCode":"InvalidAccessKeyId.NotFound",
  13. "ErrorMessage":"Specified access key is not found."
  14. }

正确返回说明:(下面五个变量将构成了一个Token)

  • StatusCode:表示获取Token的状态,获取成功时,返回值是200。
  • AccessKeyId:表示Android/iOS应用初始化OSSClient获取的 AccessKeyId。
  • AccessKeySecret:表示Android/iOS应用初始化OSSClient获取AccessKeySecret。
  • SecurityToken:表示Android/iOS应用初始化的Token。
  • Expiration:表示该Token失效的时间。主要在Android SDK会自动判断是否失效,自动获取Token。

错误返回说明:

  • StatusCode:表示获取Token的状态,获取失败时,返回值是500。
  • ErrorCode:表示错误原因。
  • ErrorMessage:表示错误的具体信息描述。

代码示例的运行方法:

  • 对于PHP版本,将包下载解压后,修改config.json这个文件,直接运行php sts.php 即能生成Token,将程序部署到指定的地址。

  • 对于JAVA版本 (依赖于java 1.7),将包下载解压后

    运行方法:java -jar oss-token-server.jar (port)。如果不指定port(端口), 直接运行java –jar oss-token-server.jar,程序会监听7080端口。如果想让程序执行在9000端口,运行java –jar app-token-server.jar 9000 , 其他端口也类似。

用户如何体验自己的APP上传应用服务器

  1. 把程序部署起来后,记下应用服务器地址如http://abc.com:8080 将示例程序里面的应用服务器修改成上述地址。
  2. 选择数据要上传到哪个Bcuket及区域,修改示例APP程序里面相应Bucket及区域。
  3. 单击设置按钮,将配置加载。
  4. 选择图片,设置上传OSS文件名,上传。这样就可以在Android上体验OSS服务了,就能通Android示例程序将数据直接上传到OSS了。
  5. 上传成功后,可以看一下数据是否在OSS上了。

核心代码解析

OSS初始化

下面讲解如何利用Android/iOS SDK跟自己的应用服务器,请求Token。

  • Android版本
  1. //初始化一个OssService用来上传
  2. public OssService initOSS(String endpoint, String bucket, UIDisplayer displayer) {
  3. OSSCredentialProvider credentialProvider;
  4. //使用自己的获取STSToken的类
  5. //从应用服务器控件里面读取应用服务器地址
  6. String stsServer = ((EditText) findViewById(R.id.stsserver)).getText().toString();
  7. //STSGetter类,封装如何跟从应用服务器取数据,必须继承于OSSFederationCredentialProvider这个类。 取Token这个取决于您所写的APP跟应用服务器数据的协议设计。
  8. if (stsServer .equals("")) {
  9. credentialProvider = new STSGetter();
  10. }else {
  11. credentialProvider = new STSGetter(stsServer);
  12. }
  13. //获取控件上的bucket名字
  14. bucket = ((EditText) findViewById(R.id.bucketname)).getText().toString();
  15. //初始化OSSClient
  16. ClientConfiguration conf = new ClientConfiguration();
  17. conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒
  18. conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒
  19. conf.setMaxConcurrentRequest(5); // 最大并发请求数,默认5个
  20. conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次
  21. OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider, conf);
  22. return new OssService(oss, bucket, displayer);
  23. }
  • iOS版本
  1. // 初始化一个OSSClient实例
  2. - (void)ossInit {
  3. // 构造一个获取STSToken的凭证提供器
  4. id<OSSCredentialProvider> credential = [[OSSFederationCredentialProvider alloc] initWithFederationTokenGetter:^OSSFederationToken * {
  5. // 实现一个函数,同步返回从server获取到的STSToken
  6. return [self getFederationToken];
  7. }];
  8. // 用endpoint、凭证提供器初始化一个OSSClient
  9. client = [[OSSClient alloc] initWithEndpoint:endPoint credentialProvider:credential];
  10. }

移动应用从应用服务器取Token

具体APP从应用服务器取Token的方法,必须写到public OSSFederationToken getFederationToken() { }这个函数里面。

注意:这个函数的逻辑依赖于可以自己设定,但是最终结果必须返回这样一个变量 return new OSSFederationToken(ak, sk, token, expiration),其中ak, sk, token, expiration 必须是从应用服务器返回的Body获取的。

在本示例中,用户可以自定义自己的移动应用跟自己应用服务器之前的协议。

  • Android版本
  1. public OSSFederationToken getFederationToken() {
  2. String stsJson;
  3. OkHttpClient client = new OkHttpClient();
  4. Request request = new Request.Builder().url(stsServer).build();
  5. try {
  6. Response response = client.newCall(request).execute();
  7. if (response.isSuccessful()) {
  8. stsJson = response.body().string();
  9. } else {
  10. throw new IOException("Unexpected code " + response);
  11. }
  12. }
  13. catch (IOException e) {
  14. e.printStackTrace();
  15. Log.e("GetSTSTokenFail", e.toString());
  16. return null;
  17. }
  18. try {
  19. JSONObject jsonObjs = new JSONObject(stsJson);
  20. String ak = jsonObjs.getString("AccessKeyId");
  21. String sk = jsonObjs.getString("AccessKeySecret");
  22. String token = jsonObjs.getString("SecurityToken");
  23. String expiration = jsonObjs.getString("Expiration");
  24. return new OSSFederationToken(ak, sk, token, expiration);
  25. }
  26. catch (JSONException e) {
  27. Log.e("GetSTSTokenFail", e.toString());
  28. e.printStackTrace();
  29. return null;
  30. }}
  • iOS版本
  1. NSURL * url = [NSURL URLWithString:STSServer];
  2. NSURLRequest * request = [NSURLRequest requestWithURL:url];
  3. OSSTaskCompletionSource * tcs = [OSSTaskCompletionSource taskCompletionSource];
  4. NSURLSession * session = [NSURLSession sharedSession];
  5. NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request
  6. completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  7. if (error) {
  8. [tcs setError:error];
  9. return;
  10. }
  11. [tcs setResult:data];
  12. }];
  13. [sessionTask resume];
  14. // 实现这个回调需要同步返回Token,所以要waitUntilFinished
  15. [tcs.task waitUntilFinished];
  16. if (tcs.task.error) {
  17. // 如果网络请求出错,返回nil表示无法获取到Token。该次请求OSS会失败。
  18. return nil;
  19. } else {
  20. // 从网络请求返回的内容中解析JSON串拿到Token的各个字段,组成STSToken返回
  21. NSDictionary * object = [NSJSONSerialization JSONObjectWithData:tcs.task.result
  22. options:kNilOptions
  23. error:nil];
  24. OSSFederationToken * token = [OSSFederationToken new];ni
  25. token.tAccessKey = [object objectForKey:@"AccessKeyId"];
  26. token.tSecretKey = [object objectForKey:@"AccessKeySecret"];
  27. token.tToken = [object objectForKey:@"SecurityToken"];
  28. token.expirationTimeInGMTFormat = [object objectForKey:@"Expiration"];
  29. return token;
  30. }

源码下载

示例程序

应用服务器代码示例的下载

本文导读目录