From 815fad99c879430f2f1d5f040ff0f1a0bf7a5c5a Mon Sep 17 00:00:00 2001 From: Hugh Lv Date: Thu, 9 Jan 2014 22:05:55 +0800 Subject: [PATCH 01/11] Refactoring developer guide. --- docs/README.md | 203 +++++++++++++++++++++---------------------------- 1 file changed, 87 insertions(+), 116 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6240d6e27..dc9150abd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,170 +1,141 @@ --- -title: Android SDK 使用指南 +title: Android SDK使用文档 --- -# Android SDK 使用指南 +# Android SDK使用文档 -- Android SDK 下载地址: -- Android SDK 源码地址: (请注意非 master 分支的代码在规格上可能承受变更) +## 目录 -此 Android SDK 基于 [七牛云存储官方API](http://docs.qiniu.com/api/index.html) 构建。在开发者的 Android App 工程项目中使用此 SDK 能够非常方便地将 Android 系统里边的文件快速直传到七牛云存储。 +- [概述](#overview) +- [使用场景](#use-scenario) +- [接入SDK](#integration) +- [上传文件](#simple-upload) +- [分片上传(断点续上传)](#resumable-upload) +- [线程安全性](#thread-safety) -出于安全考虑,使用此 SDK 无需设置密钥(AccessKey / SecretKey)。所有涉及到授权的操作,比如生成上传授权凭证(uploadToken)或下载授权凭证(downloadToken)均在业务服务器端进行。 + +## 概述 -业务服务器负责生成和颁发授权,此 SDK 只负责实施具体的上传业务。 +Android SDK只包含了最终用户使用场景中的必要功能。相比服务端SDK而言,客户端SDK不会包含对云存储服务的管理和配置功能。 -## 目录 +该SDK支持不低于2.1的Android版本。 -- [上传流程](#upload-flow) -- [下载流程](#download-flow) -- [接入SDK](#load) -- [使用SDK上传文件](#upload) -- [SDK 内置 demo 说明](#demo) -- [并发特性](#concurrency) -- [贡献代码](#contributing) -- [许可证](#license) + +## 使用场景 - +在使用Android SDK开发基于七牛云存储的应用之前,请注意理解合适的开发场景。客户端属于不可控的场景,非一般用户在拿到客户端后可能会对其进行反向工程,因此客户端程序中不可包含任何可能导致安全漏洞的业务逻辑和关键信息。 -## 上传流程 +我们推荐的安全模型如下所示。 -1. 业务服务器使用七牛云存储服务端编程语言(如 PHP/Python/Ruby/Java)SDK 生成 uploadToken (上传授权凭证) +![安全模型](http://developer.qiniu.com/docs/v6/api/overview/img/token.png) -2. 客户端 Android 使用该 uploadToken 调用此 Android 封装的上传方法直传文件到七牛云存储 +开发者需要合理划分客户端程序和业务服务器的职责范围。分发给最终用户的客户端程序中不应有需要使用管理凭证及SecretKey的场景。这些可能导致安全风险的使用场景均应被设计为在业务服务器上进行。 -3. 文件直传成功,七牛云存储向 uploadToken 生成之前所指定的业务服务器地址发起回调 +更多的相关内容请查看[编程模型](http://developer.qiniu.com/docs/v6/api/overview/programming-model.html)和[安全机制](http://developer.qiniu.com/docs/v6/api/overview/security.html)。 -4. 业务服务器接收来自七牛云存储回调的 POST 请求,处理相关 POST 参数,最后响应输出一段 JSON + +## 接入SDK -5. 七牛云存储接收业务服务器响应输出的这段 JSON,原封不动地通过 HTTP 返回给 Android 客户端程序 +> TODO: 如何将该SDK整合到工作项目中?使用jar包?拷贝源文件? + +## 上传文件 -注意事项: +开发者可以选择SDK提供的两种上传方式:表单上传和分片上传。表单上传使用一个HTTP POST请求完成文件的上传,因此比较适合较小的文件和较好的网络环境。相比而言,分片上传更能适应不稳定的网络环境,也比较适合上传比较大的文件(数百MB或更大)。 -- 此 Android SDK 当前只提供上传方法,即负责上述流程中的第2个步骤。 -- 业务服务器响应回调请求后输出 JSON,HTTP Headers 必须输出 `Content-Type` 为 `application/json`。 -- 文件上传成功后,业务服务器输出的 JSON 数据,可从所调用SDK上传代码的返回值中获取到。 +若需深入了解上传方式之间的区别,请查看[上传类型](http://developer.qiniu.com/docs/v6/api/overview/up/upload-models.html#upload-types),[表单上传接口说明](http://developer.qiniu.com/docs/v6/api/overview/up/form-upload.html),[分片上传接口说明(断点续上传)](http://developer.qiniu.com/docs/v6/api/overview/up/chunked-upload.html)。 + +### 表单上传 - +开发者可以通过调用`IO.put()`方法来以表单形式上传一个文件。该方法的详细说明如下: -## 下载流程 +``` +public void put(String key, + InputStreamAt isa, + PutExtra extra, + JSONObjectRet ret); +``` -此 Android SDK 没有提供下载文件的方法。所有上传到七牛云存储的文件,都能以如下方式进行访问: +参数说明: -公开资源: +参数 | 类型 | 说明 +:---: | :----: | :--- +key | String | 将保存为的资源唯一标识。请参见[关键概念:键值对](http://developer.qiniu.com/docs/v6/api/overview/concepts.html#key-value)。 +isa | InputStreamAt | 待上传的本地文件。 +extra | PutExtra | 额外配置项,用于精确控制上传行为。请参见[高级设置](#upload-config)。 +ret | JSONObjectRet | 开发者需实现该接口以获取上传进度和上传结果。
若上传成功,该接口中的`onSuccess()`方法将被调用。否则`onFailure()`方法将被调用。 - http:/// +表单上传的示例代码请参见SDK示例中[MyActivity.doUpload()](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyActivity.java)方法的实现。 -私有资源: + +### 分片上传 - http:///?token= +顾名思义,分片上传会将一个文件划分为多个指定大小的数据块,分别上传。分片上传的关键价值在于可更好的适应不稳定的网络环境,以及成功上传超大的文件。分片上传功能也是实现断点续上传功能的基础。 -其中\是bucket所对应的域名。七牛云存储为每一个bucket提供一个默认域名。默认域名可以到[七牛云存储开发者平台](https://portal.qiniu.com/)中,空间设置的域名设置一节查询。 +开发者可以通过调用`ResumableIO.put()`方法以分片形式上传一个文件。该方法签名和`IO.put()`一致。 -出于安全考虑,此 SDK 不提供 `downloadToken` 的生成。除 Android / iOS SDK 以外,七牛云存储其他编程语言的 SDK 都有提供签发私有资源下载授权凭证(downloadToken)的实现。 +分片上传的示例代码请参见SDK示例中[MyResumableActivity.doResumableUpload()](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyResumableActivity.java)方法的实现。 -**注意: key必须采用utf8编码,如使用非utf8编码访问七牛云存储将反馈错误** + +### 断点续上传 - +开发者可以基于分片上传机制实现断点续上传功能。 -## 接入SDK +> TODO: 该SDK是否已经支持断点续上传功能?基本的要求是可反馈完整的进度信息给开发者进行持久化,并且在上传时可以传入之前持久化的上传进度信息。 -本SDK的开发环境是 [Intellij IDEA](http://www.jetbrains.com/idea/),如果开发者使用的编辑器同为 IDEA, 直接打开项目即可,对于使用 [Eclipse](http://www.eclipse.org/) 编辑器的开发者,可以尝试导入项目。 + +### 上传中的并发性 -导入后,填写相关必要参数即可运行SDK自带的 demo 程序,配置方法见 [SDK 内置 demo 说明](#demo) 。 +分片上传机制也提供了对一个文件并发上传的能力。 +> TODO: 现在这个SDK可以设置并发数量吗? - - -## 使用SDK上传文件 - -在 Android 中选择文件一般是通过 uri 作为路径, 一般调用以下代码 - -```{java} -// 在七牛绑定的对应bucket的域名. 默认是bucket.qiniudn.com -public static String bucketName = "bucketName"; -public static String domain = bucketName + ".qiniudn.com"; -// upToken 这里需要自行获取. SDK 将不实现获取过程. 当token过期后才再获取一遍 -public String UP_TOKEN = "token"; - -boolean uploading = false; -/** - * 普通上传文件 - * @param uri - */ -private void doUpload(Uri uri) { - if (uploading) { - hint.setText("上传中,请稍后"); - return; - } - uploading = true; - String key = IO.UNDEFINED_KEY; // 自动生成key - PutExtra extra = new PutExtra(); - extra.checkCrc = PutExtra.AUTO_CRC32; - extra.params.put("x:arg", "value"); - hint.setText("上传中"); - IO.putFile(this, UP_TOKEN, key, uri, extra, new JSONObjectRet() { - @Override - public void onSuccess(JSONObject resp) { - uploading = false; - String hash; - String value; - try { - hash = resp.getString("hash"); - value = resp.getString("x:arg"); - } catch (Exception ex) { - hint.setText(ex.getMessage()); - return; - } - String redirect = "http://" + domain + "/" + hash; - hint.setText("上传成功! " + hash); - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirect)); - startActivity(intent); - } - - @Override - public void onFailure(Exception ex) { - uploading = false; - hint.setText("错误: " + ex.getMessage()); - } - }); -} -``` - + +### 高级设置 - +几种不同的上传类型都支持上传时的参数配置,使用一个统一的`PutExtra`类型来管理。除了需要指定几个最基本的上传参数(哪个文件以及上传到哪里等)外,开发者还可以通过制定一系列高级参数来灵活的控制上传的后续动作和通过变量来传递一些特定信息。 -## SDK 内置 demo 说明 +设置方法请参见[`PutExtra`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/resumableio/PutExtra.java)。开发者可以在调用`ResumableIO.put()`前往`PutExtra.params`中添加对应的参数即可,例如: -注意:demo 程序无法直接运行,需要配置 `UpToken`, `BucketName`, `Domain`信息, 将其填写到 MyActivity 之中。`key`值可以在操作界面修改。当文件上传成功时,会试图跳转到浏览器访问已经上传的资源。如果失败,会toast提示。 +``` +extra.params = new HashMap(); +extra.params.put("x:a", "bb"); // 设置一个自定义变量 +``` + +#### 上传后续动作 - +关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。上传的后续动作的设置通过在`PutExtra`中设置相应的参数来进行。对于Android开发者而言,这些后续动作都有各自的合适使用场景:[自定义响应内容](http://developer.qiniu.com/docs/v6/api/overview/up/response/response-body.html),[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html),[数据预处理](http://developer.qiniu.com/docs/v6/api/overview/up/response/persistent-op.html),[回调](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。 -## 并发特性 +对这些后续动作的合理组合使用可以大幅降低业务流程复杂度,并提升业务的健壮性。 -此 Android SDK 不是线程安全的,请勿在没有保护的情况下跨线程使用。 +举例说明,如果用户上传的是一个xxx格式的视频文件,开发者可以设置让该视频文件上传完成后转码为设定的目标格式。对应的设置项如下所示: +参数名称 | 参数内容 | 说明 +:---: | :----: | :--- +persistentOp | TODO:xxxxx | 符合数据处理规范的指令。这个指令表示要将码率调整为xxx,分辨率调整为xxx。 +persistentNotifyUrl | TODO:xxxxx | 结果通知地址,通常是向业务服务器发送该指定的请求。 - +> TODO:填写真实有效的一个示例。 -## 贡献代码 +完整的可设置参数和规格请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。 -1. Fork -2. 创建您的特性分支 (`git checkout -b my-new-feature`) -3. 提交您的改动 (`git commit -am 'Added some feature'`) -4. 将您的修改记录提交到远程 `git` 仓库 (`git push origin my-new-feature`) -5. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request + +#### 变量 +变量分为魔法变量和自定义变量,可帮助开发者快速的在客户端、业务服务器、云存储服务之间传递资源元信息。关于变量的作用,请参见[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html)。 - +如同上面已经给出的示例,如果开发者需要配置变量,只需在调用上传方法前在`PutExtra.params`中添加相应的参数即可。 -## 许可证 + +## 下载文件 -Copyright (c) 2013 www.qiniu.com +该SDK并未提供下载文件相关的功能接口,因为文件下载是一个标准的HTTP GET过程。开发者只需理解资源URI的组成格式即可非常方便的构建资源URI,并在必要的时候加上下载凭证,即可使用HTTP GET请求获取相应资源。 -基于 MIT 协议发布: +具体做法请参见[资源下载](http://developer.qiniu.com/docs/v6/api/overview/dn/download.html)和[资源下载的安全机制](http://developer.qiniu.com/docs/v6/api/overview/dn/security.html)。 -* [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT) + +## 线程安全性 +此 Android SDK 不是线程安全的,请勿在没有保护的情况下跨线程使用。 From dfd261476c6683218514ba6f2cf86f8ffdf784a9 Mon Sep 17 00:00:00 2001 From: Cheney Date: Fri, 10 Jan 2014 00:24:29 +0800 Subject: [PATCH 02/11] Update README.md --- docs/README.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/README.md b/docs/README.md index dc9150abd..02fce0e62 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,7 +18,7 @@ title: Android SDK使用文档 Android SDK只包含了最终用户使用场景中的必要功能。相比服务端SDK而言,客户端SDK不会包含对云存储服务的管理和配置功能。 -该SDK支持不低于2.1的Android版本。 +该SDK支持不低于2.2的Android版本(api8)。 ## 使用场景 @@ -36,7 +36,7 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 ## 接入SDK -> TODO: 如何将该SDK整合到工作项目中?使用jar包?拷贝源文件? +该SDK没有包含工程文件,这时需要自己新建一个工程,然后将src里面的代码复制到代码目录里面。 ## 上传文件 @@ -53,7 +53,7 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 ``` public void put(String key, InputStreamAt isa, - PutExtra extra, + com.qiniu.io.PutExtra extra, JSONObjectRet ret); ``` @@ -64,7 +64,7 @@ public void put(String key, key | String | 将保存为的资源唯一标识。请参见[关键概念:键值对](http://developer.qiniu.com/docs/v6/api/overview/concepts.html#key-value)。 isa | InputStreamAt | 待上传的本地文件。 extra | PutExtra | 额外配置项,用于精确控制上传行为。请参见[高级设置](#upload-config)。 -ret | JSONObjectRet | 开发者需实现该接口以获取上传进度和上传结果。
若上传成功,该接口中的`onSuccess()`方法将被调用。否则`onFailure()`方法将被调用。 +ret | JSONObjectRet | 开发者需实现该接口以获取上传进度和上传结果。
若上传成功,该接口中的`onSuccess()`方法将被调用。否则`onFailure()`方法将被调用。 `onProgress()`会在文件上传量发生更改的时候被调用,而且处于MainThread环境之中,可以直接操作ProgressBar之类的进度提示控件。 表单上传的示例代码请参见SDK示例中[MyActivity.doUpload()](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyActivity.java)方法的实现。 @@ -82,14 +82,22 @@ ret | JSONObjectRet | 开发者需实现该接口以获取上传进度和上传 开发者可以基于分片上传机制实现断点续上传功能。 -> TODO: 该SDK是否已经支持断点续上传功能?基本的要求是可反馈完整的进度信息给开发者进行持久化,并且在上传时可以传入之前持久化的上传进度信息。 +``` +class ResumableIO { + public static void put(String key, + InputStreamAt isa, + com.qiniu.resumableio.PutExtra extra, + JSONObjectRet ret); +} +``` +具体用法和`IO.put`的类似。 ### 上传中的并发性 分片上传机制也提供了对一个文件并发上传的能力。 -> TODO: 现在这个SDK可以设置并发数量吗? +> 目前底层采用AsyncTask来完成异步操作,系统底层默认是使用单线程来串行运行所有的AsyncTask,所以如果需要真正意义上的多线程上传,需要将AsyncTask放入线程池, 详细操作请参考[这里](http://developer.android.com/reference/android/os/AsyncTask.html) ### 高级设置 @@ -110,14 +118,6 @@ extra.params.put("x:a", "bb"); // 设置一个自定义变量 对这些后续动作的合理组合使用可以大幅降低业务流程复杂度,并提升业务的健壮性。 -举例说明,如果用户上传的是一个xxx格式的视频文件,开发者可以设置让该视频文件上传完成后转码为设定的目标格式。对应的设置项如下所示: - -参数名称 | 参数内容 | 说明 -:---: | :----: | :--- -persistentOp | TODO:xxxxx | 符合数据处理规范的指令。这个指令表示要将码率调整为xxx,分辨率调整为xxx。 -persistentNotifyUrl | TODO:xxxxx | 结果通知地址,通常是向业务服务器发送该指定的请求。 - -> TODO:填写真实有效的一个示例。 完整的可设置参数和规格请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。 @@ -138,4 +138,6 @@ persistentNotifyUrl | TODO:xxxxx | 结果通知地址,通常是向业务服务 ## 线程安全性 -此 Android SDK 不是线程安全的,请勿在没有保护的情况下跨线程使用。 +Android 一般的情况下会使用一个主线程来控制UI,非主线程无法控制UI,在Android4.0+之后必须不能在主线程完成网络请求, +该SDK是根据以上的使用场景设计,所有网络的操作均使用AsyncTask异步运行,所有回调函数又都回到了主线程(onSuccess, onFailure, onProgress),在回调函数内可以直接操作UI控件。 +如果您没有额外使用`new Thread()`等命令,该SDK将不会发生线程安全性问题。 From 83d63e827c8afa2734c4273a3c56b8d19d9fa19f Mon Sep 17 00:00:00 2001 From: Hugh Lv Date: Fri, 10 Jan 2014 19:47:35 +0800 Subject: [PATCH 03/11] Refined doc. --- docs/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index 02fce0e62..31568eb44 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,7 +25,7 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 在使用Android SDK开发基于七牛云存储的应用之前,请注意理解合适的开发场景。客户端属于不可控的场景,非一般用户在拿到客户端后可能会对其进行反向工程,因此客户端程序中不可包含任何可能导致安全漏洞的业务逻辑和关键信息。 -我们推荐的安全模型如下所示。 +我们推荐的安全模型如下所示: ![安全模型](http://developer.qiniu.com/docs/v6/api/overview/img/token.png) @@ -33,6 +33,11 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 更多的相关内容请查看[编程模型](http://developer.qiniu.com/docs/v6/api/overview/programming-model.html)和[安全机制](http://developer.qiniu.com/docs/v6/api/overview/security.html)。 + +## 安全性 + +该SDK未包含凭证生成相关的功能。开发者对安全性的控制应遵循[安全机制](http://developer.qiniu.com/docs/v6/api/overview/security.html)中建议的做法,即客户端应向业务服务器请求上传和下载凭证,而不是直接在客户端使用AccessKey/SecretKey生成对应的凭证。在客户端使用SecretKey会导致严重的安全隐患。 + ## 接入SDK @@ -97,7 +102,7 @@ class ResumableIO { 分片上传机制也提供了对一个文件并发上传的能力。 -> 目前底层采用AsyncTask来完成异步操作,系统底层默认是使用单线程来串行运行所有的AsyncTask,所以如果需要真正意义上的多线程上传,需要将AsyncTask放入线程池, 详细操作请参考[这里](http://developer.android.com/reference/android/os/AsyncTask.html) +目前本SDK的实现采用AsyncTask来进行异步操作,而Android系统底层默认是使用单线程来串行运行所有的AsyncTask,所以如果需要真正意义上的多线程上传,需要将AsyncTask放入线程池, 详细操作请参考[这里](http://developer.android.com/reference/android/os/AsyncTask.html)。 ### 高级设置 @@ -110,16 +115,12 @@ class ResumableIO { extra.params = new HashMap(); extra.params.put("x:a", "bb"); // 设置一个自定义变量 ``` - #### 上传后续动作 -关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。上传的后续动作的设置通过在`PutExtra`中设置相应的参数来进行。对于Android开发者而言,这些后续动作都有各自的合适使用场景:[自定义响应内容](http://developer.qiniu.com/docs/v6/api/overview/up/response/response-body.html),[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html),[数据预处理](http://developer.qiniu.com/docs/v6/api/overview/up/response/persistent-op.html),[回调](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。 - -对这些后续动作的合理组合使用可以大幅降低业务流程复杂度,并提升业务的健壮性。 - +关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。上传的后续动作的设置通过在`PutExtra`中设置相应的参数来进行。对于Android开发者而言,这些后续动作都有各自的合适使用场景:[自定义响应内容](http://developer.qiniu.com/docs/v6/api/overview/up/response/response-body.html),[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html),[数据预处理](http://developer.qiniu.com/docs/v6/api/overview/up/response/persistent-op.html),[回调](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。对这些后续动作的合理组合使用可以大幅降低业务流程复杂度,并提升业务的健壮性。 -完整的可设置参数和规格请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。 +开发者可以在生成上传凭证前通过配置上传策略以控制上传后续动作,该工作在业务服务器端进行,因此非本SDK的功能范畴。完整的内容请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。 #### 变量 From b395331092781a74e1b7060a12573d04af9e7dd6 Mon Sep 17 00:00:00 2001 From: Hugh Lv Date: Fri, 10 Jan 2014 20:51:33 +0800 Subject: [PATCH 04/11] Refined doc. --- docs/README.md | 67 ++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/docs/README.md b/docs/README.md index 31568eb44..228ef20ee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,8 +9,12 @@ title: Android SDK使用文档 - [概述](#overview) - [使用场景](#use-scenario) - [接入SDK](#integration) -- [上传文件](#simple-upload) -- [分片上传(断点续上传)](#resumable-upload) +- [安全性](#security) +- [上传文件](#upload) + - [表单上传](#form-upload) + - [分片上传](#chunked-upload) + - [断点续上传](#resumable-upload) +- [下载文件](#download) - [线程安全性](#thread-safety) @@ -23,7 +27,7 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 ## 使用场景 -在使用Android SDK开发基于七牛云存储的应用之前,请注意理解合适的开发场景。客户端属于不可控的场景,非一般用户在拿到客户端后可能会对其进行反向工程,因此客户端程序中不可包含任何可能导致安全漏洞的业务逻辑和关键信息。 +在使用Android SDK开发基于七牛云存储的应用之前,请理解正确的开发模型。客户端属于不可控的场景,恶意用户在拿到客户端后可能会对其进行反向工程,因此客户端程序中不可包含任何可能导致安全漏洞的业务逻辑和关键信息。 我们推荐的安全模型如下所示: @@ -33,15 +37,19 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 更多的相关内容请查看[编程模型](http://developer.qiniu.com/docs/v6/api/overview/programming-model.html)和[安全机制](http://developer.qiniu.com/docs/v6/api/overview/security.html)。 + +## 接入SDK + +该SDK没有包含工程文件,这时需要自己新建一个工程,然后将src里面的代码复制到代码目录里面。 + ## 安全性 该SDK未包含凭证生成相关的功能。开发者对安全性的控制应遵循[安全机制](http://developer.qiniu.com/docs/v6/api/overview/security.html)中建议的做法,即客户端应向业务服务器请求上传和下载凭证,而不是直接在客户端使用AccessKey/SecretKey生成对应的凭证。在客户端使用SecretKey会导致严重的安全隐患。 - -## 接入SDK +开发者可以在生成上传凭证前通过配置上传策略以控制上传的后续动作,比如在上传完成后通过回调机制通知业务服务器。该工作在业务服务器端进行,因此非本SDK的功能范畴。 -该SDK没有包含工程文件,这时需要自己新建一个工程,然后将src里面的代码复制到代码目录里面。 +完整的内容请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。 ## 上传文件 @@ -68,9 +76,26 @@ public void put(String key, :---: | :----: | :--- key | String | 将保存为的资源唯一标识。请参见[关键概念:键值对](http://developer.qiniu.com/docs/v6/api/overview/concepts.html#key-value)。 isa | InputStreamAt | 待上传的本地文件。 -extra | PutExtra | 额外配置项,用于精确控制上传行为。请参见[高级设置](#upload-config)。 +extra | PutExtra | 上传参数。可以设置MIME类型等。 ret | JSONObjectRet | 开发者需实现该接口以获取上传进度和上传结果。
若上传成功,该接口中的`onSuccess()`方法将被调用。否则`onFailure()`方法将被调用。 `onProgress()`会在文件上传量发生更改的时候被调用,而且处于MainThread环境之中,可以直接操作ProgressBar之类的进度提示控件。 +开发者可以在调用方法前构造一个[`PutExtra`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/resumableio/PutExtra.java)对象,往`PutExtra.params`中添加对应的上传参数以控制上传行为。可以设置的参数如下: + +参数 | 类型 | 说明 +:---: | :----: | :--- +mimeType | String | 指定上传文件的MIME类型。如果未指定,服务端将做自动检测。一般情况下无需设置。 +crc32 | long | 本文件的CRC校验码。服务端在上传完成后可以进行一次校验确认文件的完整性。 +params | HashMap | 可设置魔法变量和自定义变量。变量可帮助开发者快速的在客户端、业务服务器、云存储服务之间传递资源元信息。详见[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html)。 + +以下是一个关于`PutExtra`使用的示例: + +``` +extra.mimeType = "application/json"; // 强制设置MIME类型 + +extra.params = new HashMap(); +extra.params.put("x:a", "bb"); // 设置一个自定义变量 +``` + 表单上传的示例代码请参见SDK示例中[MyActivity.doUpload()](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyActivity.java)方法的实现。 @@ -95,8 +120,11 @@ class ResumableIO { JSONObjectRet ret); } ``` + 具体用法和`IO.put`的类似。 +> TODO: 这个没写完。这一节应该要告诉开发者,要断点续传的话应该持久化哪些东西,然后恢复上传时需要将之前持久化的东西设置到哪里去。 + ### 上传中的并发性 @@ -104,31 +132,6 @@ class ResumableIO { 目前本SDK的实现采用AsyncTask来进行异步操作,而Android系统底层默认是使用单线程来串行运行所有的AsyncTask,所以如果需要真正意义上的多线程上传,需要将AsyncTask放入线程池, 详细操作请参考[这里](http://developer.android.com/reference/android/os/AsyncTask.html)。 - -### 高级设置 - -几种不同的上传类型都支持上传时的参数配置,使用一个统一的`PutExtra`类型来管理。除了需要指定几个最基本的上传参数(哪个文件以及上传到哪里等)外,开发者还可以通过制定一系列高级参数来灵活的控制上传的后续动作和通过变量来传递一些特定信息。 - -设置方法请参见[`PutExtra`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/resumableio/PutExtra.java)。开发者可以在调用`ResumableIO.put()`前往`PutExtra.params`中添加对应的参数即可,例如: - -``` -extra.params = new HashMap(); -extra.params.put("x:a", "bb"); // 设置一个自定义变量 -``` - -#### 上传后续动作 - -关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。上传的后续动作的设置通过在`PutExtra`中设置相应的参数来进行。对于Android开发者而言,这些后续动作都有各自的合适使用场景:[自定义响应内容](http://developer.qiniu.com/docs/v6/api/overview/up/response/response-body.html),[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html),[数据预处理](http://developer.qiniu.com/docs/v6/api/overview/up/response/persistent-op.html),[回调](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。对这些后续动作的合理组合使用可以大幅降低业务流程复杂度,并提升业务的健壮性。 - -开发者可以在生成上传凭证前通过配置上传策略以控制上传后续动作,该工作在业务服务器端进行,因此非本SDK的功能范畴。完整的内容请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。 - - -#### 变量 - -变量分为魔法变量和自定义变量,可帮助开发者快速的在客户端、业务服务器、云存储服务之间传递资源元信息。关于变量的作用,请参见[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html)。 - -如同上面已经给出的示例,如果开发者需要配置变量,只需在调用上传方法前在`PutExtra.params`中添加相应的参数即可。 - ## 下载文件 From df4c79574abffbb600d1a4866562d651dc8d922f Mon Sep 17 00:00:00 2001 From: Hugh Lv Date: Sat, 11 Jan 2014 01:07:33 +0800 Subject: [PATCH 05/11] Refined doc. --- docs/README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/README.md b/docs/README.md index 228ef20ee..bba4eacdd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -49,7 +49,7 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 开发者可以在生成上传凭证前通过配置上传策略以控制上传的后续动作,比如在上传完成后通过回调机制通知业务服务器。该工作在业务服务器端进行,因此非本SDK的功能范畴。 -完整的内容请参见[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)。关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。 +完整的内容请参考[上传策略规格](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html),[上传凭证规格](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html),[下载凭证规格](http://developer.qiniu.com/docs/v6/api/reference/security/download-token.html)。关于上传后可以进行哪些后续动作,请查看[上传后续动作](http://developer.qiniu.com/docs/v6/api/overview/up/response/)。 ## 上传文件 @@ -61,7 +61,9 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 ### 表单上传 -开发者可以通过调用`IO.put()`方法来以表单形式上传一个文件。该方法的详细说明如下: +开发者可以通过调用`IO.put()`方法来以表单形式上传一个文件。使用该方式时应确认相应的资源大小合适于使用单一HTTP请求即可上传。过大的文件在使用该方式上传时比较容易出现超时失败的问题。该方式比较适合用于上传经压缩的小图片和短音频等,不适合用于上传较大的视频(比如尺寸超过100MB的)。 + +该方法的详细说明如下: ``` public void put(String key, @@ -74,18 +76,18 @@ public void put(String key, 参数 | 类型 | 说明 :---: | :----: | :--- -key | String | 将保存为的资源唯一标识。请参见[关键概念:键值对](http://developer.qiniu.com/docs/v6/api/overview/concepts.html#key-value)。 -isa | InputStreamAt | 待上传的本地文件。 -extra | PutExtra | 上传参数。可以设置MIME类型等。 -ret | JSONObjectRet | 开发者需实现该接口以获取上传进度和上传结果。
若上传成功,该接口中的`onSuccess()`方法将被调用。否则`onFailure()`方法将被调用。 `onProgress()`会在文件上传量发生更改的时候被调用,而且处于MainThread环境之中,可以直接操作ProgressBar之类的进度提示控件。 +`key` | `String` | 将保存为的资源唯一标识。请参见[关键概念:键值对](http://developer.qiniu.com/docs/v6/api/overview/concepts.html#key-value)。 +`isa` | `InputStreamAt` | 待上传的本地文件。 +`extra` | [`PutExtra`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/resumableio/PutExtra.java) | 上传参数。可以设置MIME类型等。 +`ret` | [`JSONObjectRet`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/auth/JSONObjectRet.java) | 开发者需实现该接口以获取上传进度和上传结果。
若上传成功,该接口中的`onSuccess()`方法将被调用。否则`onFailure()`方法将被调用。 `onProgress()`会在文件上传量发生更改的时候被调用,而且处于MainThread环境之中,可以直接操作ProgressBar之类的进度提示控件。 -开发者可以在调用方法前构造一个[`PutExtra`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/resumableio/PutExtra.java)对象,往`PutExtra.params`中添加对应的上传参数以控制上传行为。可以设置的参数如下: +开发者可以在调用方法前构造一个`PutExtra`对象,设置对应的上传参数以控制上传行为。可以设置的参数如下: 参数 | 类型 | 说明 :---: | :----: | :--- -mimeType | String | 指定上传文件的MIME类型。如果未指定,服务端将做自动检测。一般情况下无需设置。 -crc32 | long | 本文件的CRC校验码。服务端在上传完成后可以进行一次校验确认文件的完整性。 -params | HashMap | 可设置魔法变量和自定义变量。变量可帮助开发者快速的在客户端、业务服务器、云存储服务之间传递资源元信息。详见[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html)。 +`mimeType` | `String` | 指定上传文件的MIME类型。如果未指定,服务端将做自动检测。一般情况下无需设置。 +`crc32` | `long` | 本文件的CRC校验码。服务端在上传完成后可以进行一次校验确认文件的完整性。 +`params` | `HashMap` | 可设置魔法变量和自定义变量。变量可帮助开发者快速的在客户端、业务服务器、云存储服务之间传递资源元信息。详见[变量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html)。 以下是一个关于`PutExtra`使用的示例: @@ -130,7 +132,7 @@ class ResumableIO { 分片上传机制也提供了对一个文件并发上传的能力。 -目前本SDK的实现采用AsyncTask来进行异步操作,而Android系统底层默认是使用单线程来串行运行所有的AsyncTask,所以如果需要真正意义上的多线程上传,需要将AsyncTask放入线程池, 详细操作请参考[这里](http://developer.android.com/reference/android/os/AsyncTask.html)。 +目前本SDK的实现采用AsyncTask来进行异步操作,而Android系统底层默认是使用单线程来串行运行所有的AsyncTask。如果需要真正意义上的多线程上传,需要将AsyncTask放入线程池。详细操作请参考[这里](http://developer.android.com/reference/android/os/AsyncTask.html)。 ## 下载文件 @@ -139,6 +141,8 @@ class ResumableIO { 具体做法请参见[资源下载](http://developer.qiniu.com/docs/v6/api/overview/dn/download.html)和[资源下载的安全机制](http://developer.qiniu.com/docs/v6/api/overview/dn/security.html)。 +从安全性和代码可维护性的角度考虑,我们建议下载URL的拼装过程也在业务服务器进行,让客户端从业务服务器请求。 + ## 线程安全性 From 33db1b9602a930319547cf6bdf49675f17b1277c Mon Sep 17 00:00:00 2001 From: Hugh Lv Date: Sat, 11 Jan 2014 01:10:43 +0800 Subject: [PATCH 06/11] Refined doc. --- docs/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index bba4eacdd..6439eb5cc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -98,7 +98,7 @@ extra.params = new HashMap(); extra.params.put("x:a", "bb"); // 设置一个自定义变量 ``` -表单上传的示例代码请参见SDK示例中[MyActivity.doUpload()](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyActivity.java)方法的实现。 +表单上传的示例代码请参见SDK示例中[`MyActivity.doUpload()`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyActivity.java)方法的实现。 ### 分片上传 @@ -107,7 +107,7 @@ extra.params.put("x:a", "bb"); // 设置一个自定义变量 开发者可以通过调用`ResumableIO.put()`方法以分片形式上传一个文件。该方法签名和`IO.put()`一致。 -分片上传的示例代码请参见SDK示例中[MyResumableActivity.doResumableUpload()](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyResumableActivity.java)方法的实现。 +分片上传的示例代码请参见SDK示例中[`MyResumableActivity.doResumableUpload()`](https://github.com/qiniu/android-sdk/blob/develop/src/com/qiniu/demo/MyResumableActivity.java)方法的实现。 ### 断点续上传 @@ -147,5 +147,5 @@ class ResumableIO { ## 线程安全性 Android 一般的情况下会使用一个主线程来控制UI,非主线程无法控制UI,在Android4.0+之后必须不能在主线程完成网络请求, -该SDK是根据以上的使用场景设计,所有网络的操作均使用AsyncTask异步运行,所有回调函数又都回到了主线程(onSuccess, onFailure, onProgress),在回调函数内可以直接操作UI控件。 +该SDK是根据以上的使用场景设计,所有网络的操作均使用AsyncTask异步运行,所有回调函数又都回到了主线程(`onSuccess()`, `onFailure()`, `onProgress()`),在回调函数内可以直接操作UI控件。 如果您没有额外使用`new Thread()`等命令,该SDK将不会发生线程安全性问题。 From 69dd162afc15e347ecd76546e2a398ca83d042a9 Mon Sep 17 00:00:00 2001 From: Cheney Date: Mon, 13 Jan 2014 22:52:55 +0800 Subject: [PATCH 07/11] Update README.md --- docs/README.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6439eb5cc..383d6c0a7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -65,7 +65,7 @@ Android SDK只包含了最终用户使用场景中的必要功能。相比服务 该方法的详细说明如下: -``` +```java public void put(String key, InputStreamAt isa, com.qiniu.io.PutExtra extra, @@ -91,7 +91,7 @@ public void put(String key, 以下是一个关于`PutExtra`使用的示例: -``` +```java extra.mimeType = "application/json"; // 强制设置MIME类型 extra.params = new HashMap(); @@ -114,7 +114,7 @@ extra.params.put("x:a", "bb"); // 设置一个自定义变量 开发者可以基于分片上传机制实现断点续上传功能。 -``` +```java class ResumableIO { public static void put(String key, InputStreamAt isa, @@ -125,7 +125,35 @@ class ResumableIO { 具体用法和`IO.put`的类似。 -> TODO: 这个没写完。这一节应该要告诉开发者,要断点续传的话应该持久化哪些东西,然后恢复上传时需要将之前持久化的东西设置到哪里去。 +#### 续上传 +续上传的进度信息都储存在com.qiniu.resumableio.PutExtra. 所以当上传失败的时候,可以将PutExtra持久化下来,等到下一次上传的时候,再使用这个PutExtra,具体代码实现如下 + +失败状况 +```java +final PutExtra extra = new PutExtra(); +final String key = "key"; +final String filepath = "xx/xx/xx"; +ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSONObjectRet() { + int process; + public void onProcess(int current, int total) { + process = current/total; + } + // ...省略成功分支处理代码 + public void onFailure(Exception ex) { + // 忽略处理exception, + db.execute("INSERT INTO `table_resumable_table` (`key`, `filepath`, `extraJson`, `process`) VALUES ('" + key + "', '" + filepath + "', '" + extra.toJSON() + "', " + process + ")"); + } +}) +``` + +续传恢复 +```java +JSONObject ret = db.GetOne("SELECT * FROM `table_resumable_table` LIMIT 0, 1"); +PutExtra extra = new PutExtra(ret.optString("extraJson", "")); +String key = ret.optString("key", ""); +String filepath = ret.optString("filepath", ""); +ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSONObjectRet() {...}); +``` ### 上传中的并发性 From 30d36c26d73719225b589797b71411b48f07f14f Mon Sep 17 00:00:00 2001 From: Cheney Date: Mon, 13 Jan 2014 23:27:24 +0800 Subject: [PATCH 08/11] Update README.md --- docs/README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index 383d6c0a7..44503775d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -133,15 +133,28 @@ class ResumableIO { final PutExtra extra = new PutExtra(); final String key = "key"; final String filepath = "xx/xx/xx"; +// 准备上传 +db.execute("INSERT INTO `table_resumable_table` (`key`, `filepath`) VALUES ('" + key + "', '" + filepath + "')"); ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSONObjectRet() { int process; + private void persist() { + // 持久化 + db.execute("UPDATE `table_resumable_table` SET extra='" + extra.toJSON() + "', process=" + process + " WHERE `key`='" + key + "' and `filepath`='" + filepath + "'"); + } + public void onSuccess(JSONObject obj) { + // 上传成功,删除记录 + db.execute("DELETE FROM `table_resumable_table` WHERE `key`='" + key + "' and `filepath`='" + filepath + "'"); + } public void onProcess(int current, int total) { - process = current/total; + process = current*100/total; + // 每5%持久化一次 + if (process % 5 == 0) { + persist(); + } } - // ...省略成功分支处理代码 public void onFailure(Exception ex) { // 忽略处理exception, - db.execute("INSERT INTO `table_resumable_table` (`key`, `filepath`, `extraJson`, `process`) VALUES ('" + key + "', '" + filepath + "', '" + extra.toJSON() + "', " + process + ")"); + persist(); } }) ``` From 052248945d190278318b67c28b138483c5cdcdfc Mon Sep 17 00:00:00 2001 From: Cheney Date: Tue, 14 Jan 2014 10:30:40 +0800 Subject: [PATCH 09/11] Update README.md --- docs/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 44503775d..915753939 100644 --- a/docs/README.md +++ b/docs/README.md @@ -146,11 +146,12 @@ ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSON db.execute("DELETE FROM `table_resumable_table` WHERE `key`='" + key + "' and `filepath`='" + filepath + "'"); } public void onProcess(int current, int total) { - process = current*100/total; + int newProcess = current*100/total; // 每5%持久化一次 - if (process % 5 == 0) { + if (newProcess % 5 == 0 && newProcess - process > 1) { persist(); } + process = newProcess; } public void onFailure(Exception ex) { // 忽略处理exception, From f8df9930f2dd9ac6e5b8fdea355c5ded7f6dc62a Mon Sep 17 00:00:00 2001 From: Cheney Date: Tue, 14 Jan 2014 10:45:20 +0800 Subject: [PATCH 10/11] Update README.md --- docs/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index 915753939..e1bb42ca8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -130,6 +130,7 @@ class ResumableIO { 失败状况 ```java +final int PERSIST_PACE = 5; final PutExtra extra = new PutExtra(); final String key = "key"; final String filepath = "xx/xx/xx"; @@ -137,6 +138,7 @@ final String filepath = "xx/xx/xx"; db.execute("INSERT INTO `table_resumable_table` (`key`, `filepath`) VALUES ('" + key + "', '" + filepath + "')"); ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSONObjectRet() { int process; + int lastPersistProcess = 0; private void persist() { // 持久化 db.execute("UPDATE `table_resumable_table` SET extra='" + extra.toJSON() + "', process=" + process + " WHERE `key`='" + key + "' and `filepath`='" + filepath + "'"); @@ -146,12 +148,12 @@ ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSON db.execute("DELETE FROM `table_resumable_table` WHERE `key`='" + key + "' and `filepath`='" + filepath + "'"); } public void onProcess(int current, int total) { - int newProcess = current*100/total; + process = current*100/total; // 每5%持久化一次 - if (newProcess % 5 == 0 && newProcess - process > 1) { + if (process - lastPersistProcess > PERSIST_PACE) { persist(); + lastPersistProcess = process; } - process = newProcess; } public void onFailure(Exception ex) { // 忽略处理exception, From 1da2e1971dd186c3d7f39341ecbe13118e88f99d Mon Sep 17 00:00:00 2001 From: Hugh Lv Date: Tue, 14 Jan 2014 11:09:39 +0800 Subject: [PATCH 11/11] Refined doc. --- docs/README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index e1bb42ca8..1e7130168 100644 --- a/docs/README.md +++ b/docs/README.md @@ -126,11 +126,12 @@ class ResumableIO { 具体用法和`IO.put`的类似。 #### 续上传 -续上传的进度信息都储存在com.qiniu.resumableio.PutExtra. 所以当上传失败的时候,可以将PutExtra持久化下来,等到下一次上传的时候,再使用这个PutExtra,具体代码实现如下 +续上传的进度信息都储存在com.qiniu.resumableio.PutExtra. 所以当上传失败的时候,可以将PutExtra持久化下来,等到下一次上传的时候,再使用这个PutExtra,具体代码实现如下。 + +上传进度持久化: -失败状况 ```java -final int PERSIST_PACE = 5; +final int PERSIST_PACE = 5; // 每5%进度持久化一次 final PutExtra extra = new PutExtra(); final String key = "key"; final String filepath = "xx/xx/xx"; @@ -149,7 +150,7 @@ ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSON } public void onProcess(int current, int total) { process = current*100/total; - // 每5%持久化一次 + // 每特定进度持久化一次 if (process - lastPersistProcess > PERSIST_PACE) { persist(); lastPersistProcess = process; @@ -162,12 +163,16 @@ ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSON }) ``` -续传恢复 +恢复上传进度: + ```java JSONObject ret = db.GetOne("SELECT * FROM `table_resumable_table` LIMIT 0, 1"); PutExtra extra = new PutExtra(ret.optString("extraJson", "")); String key = ret.optString("key", ""); String filepath = ret.optString("filepath", ""); + +// 实际情况中,很可能出现本地文件在续传时已被删除或者修改的情况,开发者应在恢复上传前先做相应的校验。 + ResumableIO.put(key, InputStreamAt.fromFile(new File(filepath)), extra, new JSONObjectRet() {...}); ```