Android开发中使用七牛云存储进行图片上传下载 - 白少木丿

时间:2024-03-07 20:52:55

Android开发中使用七牛云存储进行图片上传下载

  Android开发中的图片存储本来就是比较耗时耗地的事情,而使用第三方的七牛云,便可以很好的解决这些后顾之忧,最近我也是在学习七牛的SDK,将使用过程在这记录下来,方便以后使用。

        

 

  先说一下七牛云的存储原理,上面这幅图片是官方给出的原理图,表述当然比较清晰了。可以看出,要进行图片上传的话可以分为五大步:

    1. 客户端用户登录到APP的账号系统里面;

    2. 客户端上传文件之前,需要向业务服务器申请七牛的上传凭证,这个凭证由业务服务器使用七牛提供的服务端SDK生成;

    3. 客户端使用七牛提供的客户端SDK,调用上传方法上传文件,上传方法中必须有上传凭证和文件内容(由于七牛允许大小为0的文件,所以文件上传之前,建议检查文件大小。如果业务不允许文件大小为0,那么需要自行检测下);

    4. 客户端文件上传到七牛之后,可选的操作是七牛回调业务服务器,(即七牛把文件相关的信息发送POST请求到上传策略里面指定的回调地址);

    5. 业务服务器回复七牛的回调请求,给出JSON格式的回复内容(必须是JSON格式的回复),这个回复内容将被七牛转发给客户端;

  好了,七牛云的运作原理搞清楚了,仔细理解一下也不是很麻烦嘛,下面我们来开始整合操作吧。

一、下载官方SDK

  参照七牛云官网(http://www.qiniu.com/?utm_campaign=baiduSEM&utm_source=baiduSEM&utm_medium=baiduSEM&utm_content=baiduSEM)下载指定SDK,其实根据官方提供的Maven地址下载就好了,在下载最新版QiniuSDK之后,是不是就可以忙着copy开发文档中的相应代码了?

  千万别急,除了依赖qiniu-android-sdk,还要依赖happy-dns,okhttp,android-async-http,这样一共是四个依赖包。这里说个小技巧,如果嫌下载那些东西麻烦,可以将官方Demo下载下来,然后将里边的依赖包全部放到自己的项目里,当然这样做的前提是你要分得清哪些是哪些。

二、清单文件添加权限

  注意:如果使用Android5.0及其以上版本,权限是要在代码中申请的。

1 <uses-permission android:name="android.permission.INTERNET"/>
2 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

三、定义变量

  在写上传下载代码前,我们需要先定义以下几个变量。

 1     private TextView title;  //显示上传结果
 2     private ImageView image;  //显示下载的图片内容
 3     private ProgressDialog progressDialog;  //上传进度提示框
 4     private boolean isProgressCancel;  //网络请求过程中是否取消上传或下载
 5     private UploadManager uploadManager;  //七牛SDK的上传管理者
 6     private UploadOptions uploadOptions;  //七牛SDK的上传选项
 7     private MyUpCompletionHandler mHandler;  //七牛SDK的上传返回监听
 8     private UpProgressHandler upProgressHandler;  //七牛SDK的上传进度监听
 9     private UpCancellationSignal upCancellationSignal;  //七牛SDK的上传过程取消监听
10     private final static String TOKEN_URL = "http://xxx.xxx.xxx/x/";  //服务器请求token的网址
11     private String uptoken;  //服务器请求Token值
12     private String upKey;  //上传文件的Key值
13     private byte[] upLoadData;  //上传的文件

 

四、上传图片

  七牛服务器可以上传的有三种类型,包括byte[]类型的图片,String类型的文件路径,File类型的文件;

(一)从服务器请求token

 1     private void getTokenFromService() {
 2         //模拟从服务端获取uptoken
 3         uptoken = "12343232313123";
 4         SyncHttpClient client = new SyncHttpClient();
 5         client.get(TOKEN_URL, new TextHttpResponseHandler() {
 6             @Override
 7             public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
 8                 Log.e("Error", "onFailure: 服务器请求Token失败");
 9             }
10 
11             @Override
12             public void onSuccess(int statusCode, Header[] headers, String responseString) {
13                 try {
14                     JSONObject jsonObject = new JSONObject(responseString);
15                     //解析得到的Json串,获取token值
16                     uptoken = jsonObject.getString("token");
17                 } catch (JSONException e) {
18                     e.printStackTrace();
19                 }
20             }
21         });
22     }

(二)初始化上传参数

 1     private void initData() {
 2         getTokenFromService();
 3         upKey = getPicture();
 4         uploadManager = new UploadManager();
 5         upProgressHandler = new UpProgressHandler() {
 6             /**
 7              * @param key 上传时的upKey;
 8              * @param percent 上传进度;
 9              */
10             @Override
11             public void progress(String key, double percent) {
12                 progressDialog.setProgress((int) (upLoadData.length * percent));
13             }
14         };
15         upCancellationSignal = new UpCancellationSignal() {
16             @Override
17             public boolean isCancelled() {
18                 return isProgressCancel;
19             }
20         };
21         //定义数据或文件上传时的可选项
22         uploadOptions = new UploadOptions(
23                 null,  //扩展参数,以<code>x:</code>开头的用户自定义参数
24                 "mime_type",  //指定上传文件的MimeType
25                 true,  //是否启用上传内容crc32校验
26                 upProgressHandler,  //上传内容进度处理
27                 upCancellationSignal  //取消上传信号
28         );
29         mHandler = new MyUpCompletionHandler();
30     }

(三)启动异步线程,上传图片文件

 1     public void clickPost(View view) {
 2         if (TextUtils.isEmpty(uptoken)) {
 3             Toast.makeText(MainActivity.this, "正在从网络获取Token值,请稍后...", Toast.LENGTH_SHORT).show();
 4             return;
 5         }
 6         new Thread(new Runnable() {
 7             @Override
 8             public void run() {
 9                 progressDialog.setMax(upLoadData.length);
10                 progressDialog.show();
11                 uploadManager.put(upLoadData, upKey, uptoken, mHandler, uploadOptions);
12             }
13         }).start();
14     }

五、下载图片

  该 SDK 并未提供下载文件相关的功能接口,因为文件下载是一个标准的 HTTP GET 过程。开发者只需理解资源 URI 的组成格式即可非常方便的构建资源 URI,并在必要的时候加上下载凭证,即可使用 HTTP GET 请求获取相应资源。

  上段斜体是从QiniuSDK官网的指导文档中复制的,所以下载方式比较简单。

 1     public void clickDown(View view) {
 2         //图片上传到七牛之后,
 3         // 默认会将文件的hash和key(文件的文件名)响应回来,
 4         // 然后在空间设置->域名设置里,找到空间域名,
 5         // 通过http://空间域名/key的形式,拿到文件的url。
 6         String fileName = "xxx.xxx.xx/xx";
 7         String downUrl = "http://" + fileName + "/" + upKey;
 8         SyncHttpClient client = new SyncHttpClient();
 9         client.get(downUrl, new BinaryHttpResponseHandler() {
10             @Override
11             public void onSuccess(int statusCode, Header[] headers, byte[] binaryData) {
12                 if (binaryData != null) {
13                     image.setImageBitmap(BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length));
14                 }
15             }
16             @Override
17             public void onFailure(int statusCode, Header[] headers, byte[] binaryData, Throwable error) {
18                 Log.e("Error", "onFailure: 图片下载失败" );
19             }
20         });
21     }

六、文档总结

  有时候看一百遍文字介绍,也不如读一遍Fuck Code,所以我还是把涉及的文件源码也copy过来一份,以后也方便看了。

(一)MainActivity.class

  1 package com.example.administrator;
  2 
  3 import android.app.ProgressDialog;
  4 import android.content.DialogInterface;
  5 import android.graphics.BitmapFactory;
  6 import android.os.Bundle;
  7 import android.support.v7.app.AppCompatActivity;
  8 import android.text.TextUtils;
  9 import android.util.Log;
 10 import android.view.View;
 11 import android.widget.ImageView;
 12 import android.widget.TextView;
 13 import android.widget.Toast;
 14 
 15 import com.example.administrator.myqiniudemo.R;
 16 import com.loopj.android.http.BinaryHttpResponseHandler;
 17 import com.loopj.android.http.SyncHttpClient;
 18 import com.loopj.android.http.TextHttpResponseHandler;
 19 import com.qiniu.android.http.ResponseInfo;
 20 import com.qiniu.android.storage.UpCancellationSignal;
 21 import com.qiniu.android.storage.UpCompletionHandler;
 22 import com.qiniu.android.storage.UpProgressHandler;
 23 import com.qiniu.android.storage.UploadManager;
 24 import com.qiniu.android.storage.UploadOptions;
 25 
 26 import org.json.JSONException;
 27 import org.json.JSONObject;
 28 
 29 import cz.msebera.android.httpclient.Header;
 30 
 31 public class MainActivity extends AppCompatActivity {
 32 
 33     private TextView title;  //显示上传结果
 34     private ImageView image;  //显示下载的图片内容
 35     private ProgressDialog progressDialog;  //上传进度提示框
 36     private boolean isProgressCancel;  //网络请求过程中是否取消上传或下载
 37     private UploadManager uploadManager;  //七牛SDK的上传管理者
 38     private UploadOptions uploadOptions;  //七牛SDK的上传选项
 39     private MyUpCompletionHandler mHandler;  //七牛SDK的上传返回监听
 40     private UpProgressHandler upProgressHandler;  //七牛SDK的上传进度监听
 41     private UpCancellationSignal upCancellationSignal;  //七牛SDK的上传过程取消监听
 42     private final static String TOKEN_URL = "http://xxx.xxx.xxx/x/";  //服务器请求token的网址
 43     private String uptoken;  //服务器请求Token值
 44     private String upKey;  //上传文件的Key值
 45     private byte[] upLoadData;  //上传的文件
 46 
 47     @Override
 48     protected void onCreate(Bundle savedInstanceState) {
 49         super.onCreate(savedInstanceState);
 50         setContentView(R.layout.activity_main);
 51         initView();
 52         initData();
 53     }
 54 
 55     private void initData() {
 56         getTokenFromService();
 57         upKey = getPicture();
 58         uploadManager = new UploadManager();
 59         upProgressHandler = new UpProgressHandler() {
 60             /**
 61              * @param key 上传时的upKey;
 62              * @param percent 上传进度;
 63              */
 64             @Override
 65             public void progress(String key, double percent) {
 66                 progressDialog.setProgress((int) (upLoadData.length * percent));
 67             }
 68         };
 69         upCancellationSignal = new UpCancellationSignal() {
 70             @Override
 71             public boolean isCancelled() {
 72                 return isProgressCancel;
 73             }
 74         };
 75         //定义数据或文件上传时的可选项
 76         uploadOptions = new UploadOptions(
 77                 null,  //扩展参数,以<code>x:</code>开头的用户自定义参数
 78                 "mime_type",  //指定上传文件的MimeType
 79                 true,  //是否启用上传内容crc32校验
 80                 upProgressHandler,  //上传内容进度处理
 81                 upCancellationSignal  //取消上传信号
 82         );
 83         mHandler = new MyUpCompletionHandler();
 84     }
 85 
 86     private String getPicture() {
 87         //模拟上传图片的byte数组,并返回文件名
 88         upLoadData = new byte[]{1, 2, 3, 1, 2, 3, 12, 3, 4, 2, 1, 2};
 89         return "upload.txt";
 90     }
 91 
 92     private void getTokenFromService() {
 93         //模拟从服务端获取uptoken
 94         uptoken = "12343232313123";
 95         SyncHttpClient client = new SyncHttpClient();
 96         client.get(TOKEN_URL, new TextHttpResponseHandler() {
 97             @Override
 98             public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
 99                 Log.e("Error", "onFailure: 服务器请求Token失败");
100             }
101 
102             @Override
103             public void onSuccess(int statusCode, Header[] headers, String responseString) {
104                 try {
105                     JSONObject jsonObject = new JSONObject(responseString);
106                     //解析得到的Json串,获取token值
107                     uptoken = jsonObject.getString("token");
108                 } catch (JSONException e) {
109                     e.printStackTrace();
110                 }
111             }
112         });
113     }
114 
115     private void initView() {
116         title = (TextView) findViewById(R.id.title);
117         image = (ImageView) findViewById(R.id.image);
118         initProgressBar();
119     }
120 
121     private void initProgressBar() {
122         progressDialog = new ProgressDialog(MainActivity.this);
123         progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
124         progressDialog.setTitle("进度提示");
125         progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener() {
126             @Override
127             public void onClick(DialogInterface dialog, int which) {
128                 isProgressCancel = true;
129             }
130         });
131     }
132 
133     /**
134      * 点击按钮,开始文件上传
135      *
136      * @param view
137      */
138     public void clickPost(View view) {
139         if (TextUtils.isEmpty(uptoken)) {
140             Toast.makeText(MainActivity.this, "正在从网络获取Token值,请稍后...", Toast.LENGTH_SHORT).show();
141             return;
142         }
143         new Thread(new Runnable() {
144             @Override
145             public void run() {
146                 progressDialog.setMax(upLoadData.length);
147                 progressDialog.show();
148                 uploadManager.put(upLoadData, upKey, uptoken, mHandler, uploadOptions);
149             }
150         });
151     }
152 
153     /**
154      * 点击按钮,开始文件下载
155      *
156      * @param view
157      */
158     public void clickDown(View view) {
159         //图片上传到七牛之后,
160         // 默认会将文件的hash和key(文件的文件名)响应回来,
161         // 然后在空间设置->域名设置里,找到空间域名,
162         // 通过http://空间域名/key的形式,拿到文件的url。
163         String fileName = "xxx.xxx.xx/xx";
164         String downUrl = "http://" + fileName + "/" + upKey;
165         SyncHttpClient client = new SyncHttpClient();
166         client.get(downUrl, new BinaryHttpResponseHandler() {
167             @Override
168             public void onSuccess(int statusCode, Header[] headers, byte[] binaryData) {
169                 if (binaryData != null) {
170                     image.setImageBitmap(BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length));
171                 }
172             }
173             @Override
174             public void onFailure(int statusCode, Header[] headers, byte[] binaryData, Throwable error) {
175                 Log.e("Error", "onFailure: 图片下载失败" );
176             }
177         });
178     }
179 
180     /**
181      * 自定义上传完成监听类
182      * 实现QiniuSDK中的UpCompletionHandler接口
183      */
184     public class MyUpCompletionHandler implements UpCompletionHandler {
185         /**
186          * @param key      上传时的upKey;
187          * @param info     Json串表示的上传信息,包括使用版本,请求状态,请求Id等信息;
188          * @param response Json串表示的文件信息,包括文件Hash码,文件Mime类型,文件大小等信息;
189          */
190         @Override
191         public void complete(String key, ResponseInfo info, JSONObject response) {
192             progressDialog.dismiss();
193             title.setText(key + "!\n" + info + "!\n" + response + "!");
194         }
195     }
196 }
View Code

(二)activity_main.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout
 3     android:orientation="vertical"
 4     xmlns:android="http://schemas.android.com/apk/res/android"
 5     xmlns:tools="http://schemas.android.com/tools"
 6     android:layout_width="match_parent"
 7     android:layout_height="match_parent"
 8     android:gravity="center_horizontal"
 9     tools:context="com.example.administrator.myqiniudemo.MainActivity">
10 
11     <TextView
12         android:id="@+id/title"
13         android:layout_width="wrap_content"
14         android:layout_height="wrap_content"
15         android:text="Hello Qiniu!"/>
16     <Button
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:text="上传图片"
20         android:onClick="clickPost"
21         />
22     <Button
23         android:layout_width="wrap_content"
24         android:layout_height="wrap_content"
25         android:text="下载图片"
26         android:onClick="clickDown"
27         />
28     <ImageView
29         android:id="@+id/image"
30         android:layout_width="match_parent"
31         android:layout_height="match_parent"/>
32 </LinearLayout>
View Code