七牛云存储之文件上传

项目中要用到七牛云存储,用于存储用户的文件数据,于是,看了一下七牛的文档(Android SDK 和 Java SDK),写了一个 demo 。本文记录一下 android 端上传文件到七牛服务器的步骤,并对七牛云存储使用的一些问题作出了一些思考。demo 实现了单个文件上传,多个文件上传,多个文件排队上传。详情请参考七牛官方文档。

一,注册七牛账号,并创建存储空间

  1. 注册
  2. 创建密钥,拿到 AccessKey/SecretKey,用于生成上传的token
  3. 创建存储空间,指定文件上传的空间名称:bucket

这里拿到的资源有:AccessKey、SecretKey、bucket,主要是这三个值。

二、Java后台生成上传文件的token

android 端可以添加七牛的 Java SDK,并可以生成 token,但是使用这个 token 上传文件一直提示:no such bucket(具体原因不知道,但是根据密钥安全使用须知,token 是不应该在APP端生成的,所以也没继续找这个问题)。可以在 android studio 新建一个 Java module,添加七牛 Java SDK 依赖,使用 SDK 生成 token,注意这个 token 是有时效的,过来一段时间后会失效,所以每次上传文件前,应该向后台请求一个上传 token,代码如下(根据不同需求,生成 token 所需参数有所变化,详情请看客户端上传凭证):

public static void createQiniuToken() {String accessKey = "your accesskey";String secretKey = "your secretkey";String bucket = "your bucket name";Auth auth = Auth.create(accessKey, secretKey);String upToken = auth.uploadToken(bucket);System.out.println(upToken);}

拿到token之后就可以在 android 端 demo 使用。

三、上传文件到七牛服务器应该考虑或者注意的问题

文件上传到七牛服务器,有很多业务要处理,获取上传凭证(token),文件的命名,文件覆盖,文件管理等。Android SDK 没有提供这些功能,这些功能在 Java SDK 中实现,通过生成不同的 token 实现不同业务功能。

3.1 关于android SDK 上传文件方法中的key

文件的外链接地址是由存储空间的外链域名和key组成的:外链域名 + key,如我的一个bucket的域名为:http://pfln1bbp9.bkt.clouddn.com/ ,那么上传到这个bucket的文件的外链地址为:http://pfln1bbp9.bkt.clouddn.com/ + key。所以,key如何取值,应该有一套自己项目的标准,确保文件何时是唯一的,何时是不唯一的。

3.2 修改或者覆盖文件该如何处理

有些文件可能会修改,那么修改后的文件如何覆盖旧文件呢?
七牛 Android SDK 并没有提供文件的覆盖功能,文件覆盖上传需要服务端的支持,即在生成token的时候带文件的名字,然后拿着这个 token 去上传文件即可:

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
String key = "file key";Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket, key);
System.out.println(upToken);

更多内容请查看 覆盖上传的凭证

3.3 文件的管理功能

文件资源管理属于Java SDK的功能,参考资源管理

3.4 视频/音频/图片压缩功能

七牛支持在文件上传到七牛之后,立即对其进行多种指令的数据处理,这个只需要在生成的上传凭证中指定相关的处理参数即可。
参考 带数据处理的凭证

3.5 文件下载

参考下载文件,值得注意的是在拼接链接之前,将文件名进行urlencode以兼容不同的字符。

3.6 是否支持批量上传

七牛目前只支持一个请求上传一个文件,所以一次上传多个文件的话,就等同于一次发送多个请求,七牛不支持。这正是本文 demo 所要解决的问题。

3.7 密钥安全使用须知

参考密钥安全使用须知

四、Android 端文件上传工具

详情请参考七牛官方文档 对象存储Android SDK。
本 demo 简单实现了七牛文件上传工具:QiniuUploadManager ,这个类提供三个上传文件的方法,和两个取消上传的方法,具体如下:

4.1 单个文件上传

public boolean upload(QiniuUploadFile param, OnUploadListener uploadListener)

4.2 多文件上传

 public boolean upload(List<QiniuUploadFile> params, OnUploadListener uploadListener)

4.3 多文件排队上传

 public void queueUpload(Queue<QiniuUploadFile> params, OnUploadListener uploadListener)

4.4 取消上传

// 取消某一个listener的任务
public void cancel(OnUploadListener listener)
// 取消所有listener的任务
public void cancel()

4.5 example

private QiniuUploadManager manager;
private String token = "your qiniu upload token";private void singleUpload(String path) {if (manager == null) {manager = QiniuUploadManager.getInstance(this);}String currentTim = String.valueOf(System.currentTimeMillis());String key = "files/" + currentTim + "/" + currentTim + ".jpg";String mimeType = "image/jpeg";QiniuUploadManager.QiniuUploadFile param = new QiniuUploadManager.QiniuUploadFile(path, key, mimeType, token);manager.upload(param, new QiniuUploadManager.OnUploadListener() {@Overridepublic void onStartUpload() {Log.e(TAG, "onStartUpload");}@Overridepublic void onUploadProgress(String key, double percent) {}@Overridepublic void onUploadFailed(String key, String err) {Log.e(TAG, "onUploadFailed:" + err);}@Overridepublic void onUploadBlockComplete(String key) {Log.e(TAG, "onUploadBlockComplete");}@Overridepublic void onUploadCompleted() {Log.e(TAG, "onUploadCompleted");}@Overridepublic void onUploadCancel() {Log.e(TAG, "onUploadCancel");}});}

4.6 完整代码


import android.content.Context;
import android.util.Log;import com.qiniu.android.common.FixedZone;
import com.qiniu.android.storage.Configuration;
import com.qiniu.android.storage.KeyGenerator;
import com.qiniu.android.storage.Recorder;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import com.qiniu.android.storage.persistent.FileRecorder;import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;/*** @Description 七牛文件上传工具* @Date 2018.09.26*/
public class QiniuUploadManager {public interface OnUploadListener {void onStartUpload();void onUploadProgress(String key, double percent);void onUploadFailed(String key, String err);void onUploadBlockComplete(String key);void onUploadCompleted();void onUploadCancel();}private final String TAG = this.getClass().getSimpleName();private static QiniuUploadManager manager;public static QiniuUploadManager getInstance(Context context) {if (manager == null) {synchronized(QiniuUploadManager.class) {if(manager == null) {manager = new QiniuUploadManager(context);}}}return manager;}private UploadManager uploadManager;private Object lock = new Object();private HashMap<OnUploadListener, Boolean> cancels = new HashMap<>();private List<OnUploadListener> uploadListeners = new ArrayList<>();private QiniuUploadManager(Context appContext) {initManager(appContext.getApplicationContext());}private void initManager(Context appContext) {String dirPath = appContext.getExternalCacheDir().getPath() + File.separator + "QiniuTemp";Log.d(TAG, dirPath);Recorder recorder = null;try {recorder = new FileRecorder(dirPath);} catch (Exception e) {e.printStackTrace();}KeyGenerator keyGen = (key, file) -> key + "_._" + new StringBuffer(file.getAbsolutePath()).reverse();Configuration.Builder builder = new Configuration.Builder();builder.chunkSize(512 * 1024)              // 分片上传时,每片的大小。 默认256K.putThreshhold(1024 * 1024)        // 启用分片上传阀值。默认512K.connectTimeout(10)                // 链接超时。默认10秒.responseTimeout(60)               // 服务器响应超时。默认60秒.zone(FixedZone.zone0);            // 设置区域,指定不同区域的上传域名、备用域名、备用IP。if (recorder != null) {builder = builder.recorder(recorder)   // recorder分片上传时,已上传片记录器。默认null.recorder(recorder, keyGen);   // keyGen 分片上传时,生成标识符,用于片记录器区分是那个文件的上传记录}Configuration config = builder.build();uploadManager = new UploadManager(config);}/*** 上传单个文件到七牛服务器** @param param* @param uploadListener* @return 文件有效,开始上传返回true,否则返回false*/public synchronized boolean upload(QiniuUploadFile param, OnUploadListener uploadListener) {if (param == null) {return false;}File uploadFile = new File(param.getFilePath());if (!uploadFile.exists() || uploadFile.isDirectory()) {return false; // 如果是文件夹,或者文件不存在,那么返回false}if (uploadListener != null) {uploadListener.onStartUpload();Log.d(TAG, "开始上传(" + param.getKey() + "): " + param.getFilePath());}// 注册回调对象,用户取消上传时使用这些对象uploadListeners.add(uploadListener);cancels.put(uploadListener, false);uploadManager.put(uploadFile, param.getKey(), param.getToken(),(key, info, response) -> {synchronized (lock) {if (uploadListener == null) {return;}if (info.isOK()) {Log.d(TAG, "上传成功(" + key +"): " + info.duration);uploadListener.onUploadBlockComplete(key);uploadListener.onUploadCompleted();} else {Log.d(TAG, "上传失败(" + key + "): " + info.error);uploadListener.onUploadFailed(key, info.error);}// 清理回调等资源Log.d(TAG, "上传完成(" + key +"): " + info.duration);uploadListeners.remove(uploadListener);cancels.remove(uploadListener);}},new UploadOptions(null, null, false,(key12, percent) -> {synchronized (lock) {Log.d(TAG, "progress(" + key12 + "):" + percent);if (uploadListener != null) {uploadListener.onUploadProgress(key12, percent);}}},() -> {synchronized (lock) {if (uploadListener == null) {return false;}Boolean result = cancels.get(uploadListener);// Log.d(TAG, "检查取消标识(" + param.getKey() + "): " + result);if (result != null && result) {cancels.remove(uploadListener);}// 有出现一次true后还继续调用的情况,需要判nullreturn result == null ? true : result;}}));return true;}/*** 同时上传多个文件* @param params 需要上传的文件* @param uploadListener 回调* @return 开始上传返回 true,如果参数无效,或者文件不存在等,返回false,不上传*/public synchronized boolean upload(List<QiniuUploadFile> params, OnUploadListener uploadListener) {if (params == null || params.size() == 0) {return false;}AtomicInteger completedCount = new AtomicInteger();  // 完成(失败也算完成)的数量List<QiniuUploadFile> needUploadFile = new ArrayList<>();for (QiniuUploadFile param : params) {File uploadFile = new File(param.getFilePath());if (!uploadFile.exists() || uploadFile.isDirectory()) {continue;  // 过滤无效的文件}needUploadFile.add(param);}if (needUploadFile.size() == 0) {return false;}if (uploadListener != null) {Log.d(TAG, "开始上传(size=" + needUploadFile.size() + ")");uploadListener.onStartUpload(); // 开始上传任务}uploadListeners.add(uploadListener);cancels.put(uploadListener, false);for (QiniuUploadFile param : needUploadFile) {File uploadFile = new File(param.getFilePath());uploadManager.put(uploadFile, param.getKey(), param.getToken(),(key, info, response) -> {synchronized (lock) {completedCount.getAndIncrement();if (uploadListener == null) {return;}if (info.isOK()) {Log.d(TAG, "上传成功(" + key +"): " + info.duration);uploadListener.onUploadBlockComplete(key);} else {Log.d(TAG, "上传失败(" + key + "): " + info.error);uploadListener.onUploadFailed(key, info.error);}if (completedCount.get() == needUploadFile.size()) {Log.d(TAG, "上传完成(" + needUploadFile.size() +")");uploadListener.onUploadCompleted();// 如果所有任务都完成了,那么清理回调资源uploadListeners.remove(uploadListener);cancels.remove(uploadListener);}}},new UploadOptions(null, param.getMimeType(), false,(key1, percent) -> {synchronized (lock) {Log.d(TAG, "progress(" + key1 + "):" + percent);if (uploadListener != null) {uploadListener.onUploadProgress(key1, percent);}}},() -> {if (uploadListener == null) {return false;}Boolean result = cancels.get(uploadListener);// Log.d(TAG, "检查取消标识(" + param.getKey() + "): " + result);if (result != null && result) {cancels.remove(uploadListener);}// 由于同时上传多个文件是共享一个:uploadListener,// 所以,后面读取到的都是null,null,标识取消return result == null ? true : result;}));}return true;}/*** 排队的方式上传文件,上传完前一个才继续上传下一个* 注意,这个方法没有 onStartUpload 回调* @param params 需要上传的文件* @param uploadListener 回调接口*/public synchronized void queueUpload(Queue<QiniuUploadFile> params, OnUploadListener uploadListener) {if (params == null || params.size() == 0) {return;}Queue<QiniuUploadFile> files = new LinkedList<>();for(QiniuUploadFile param : params) {File uploadFile = new File(param.getFilePath());if (uploadFile.exists() && !uploadFile.isDirectory()) {files.add(param);}}QiniuUploadFile param = files.poll();if(param == null) {return;}File uploadFile = new File(param.getFilePath());// 注册回调对象uploadListeners.add(uploadListener);Boolean cancel = cancels.get(uploadListener);if(cancel != null && cancel) {cancels.remove(uploadListener); // 在这里移除取消任务标志return;}cancels.put(uploadListener, false);uploadManager.put(uploadFile, param.getKey(), param.getToken(),(key, info, response) -> {synchronized (lock) {if (info.isOK()) {Log.d(TAG, "上传成功(" + key +"): " + info.duration);if(uploadListener != null) {uploadListener.onUploadBlockComplete(key);}} else {Log.d(TAG, "上传失败(" + key + "): " + info.error);if(uploadListener != null) {uploadListener.onUploadFailed(key, info.error);}}if(files.size() == 0) {// 清理回调等资源Log.d(TAG, "上传完成(" + key + "): " + info.duration);if(uploadListener != null) {uploadListeners.remove(uploadListener);cancels.remove(uploadListener);}} else {// 未上传完成,继续队列中的下一个任务queueUpload(files, uploadListener);}}},new UploadOptions(null, null, false,(key12, percent) -> {synchronized (lock) {Log.d(TAG, "progress(" + key12 + "):" + percent);if (uploadListener != null) {uploadListener.onUploadProgress(key12, percent);}}},() -> {synchronized (lock) {if (uploadListener == null) {return false;}Boolean result = cancels.get(uploadListener);//Log.d(TAG, "取消(" + param.getKey() + "): " + result);// 有出现一次true后还继续调用的情况,所以需要判nullreturn result == null ? true : result;}}));}/*** 取消指定上传任务** @param listener*/public void cancel(OnUploadListener listener) {synchronized (lock) {cancels.put(listener, true);for (OnUploadListener uploadListener : uploadListeners) {if (uploadListener == listener) {try {uploadListener.onUploadCancel();Log.d(TAG, "取消上传");} catch (Exception e) {e.printStackTrace();}break;}}uploadListeners.remove(listener);}}/*** 取消所有的上传任务*/public void cancel() {synchronized (lock) {for (OnUploadListener key : cancels.keySet()) {cancels.put(key, true);}for (OnUploadListener listener : uploadListeners) {try {listener.onUploadCancel();} catch (Exception e) {e.printStackTrace();}}uploadListeners.clear();Log.d(TAG, "取消所有上传任务");}}public static class QiniuUploadFile {private String filePath;  // 文件的路径private String key;       // 文件上传到服务器的路径,如:files/images/test.jpgprivate String mimeType;  // 文件类型private String token;     // 从后台获取的token值,只在一定时间内有效public QiniuUploadFile(String filePath, String key, String mimeType, String token) {this.filePath = filePath;this.key = key;this.mimeType = mimeType;this.token = token;}public String getFilePath() {return filePath;}public String getKey() {return key;}public String getMimeType() {return mimeType;}public String getToken() {return token;}}
}

七牛云存储之文件上传(Android)相关推荐

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

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

  2. 使用七牛云JS-SDK实现文件上传

    1.添加js文件 <script type="text/javascript" src="/resources/js/jquery/jquery-1.7.min.j ...

  3. 七牛云存储之覆盖上传

    概述: 本地文件修改,然后需要覆盖在七牛存储空间原本已经存在的文件,那么这时需要使用七牛提供的覆盖上传: 问题: 如何实现,以及实现思路? 思路: 1.七牛提供上传策略,指定scope为< bu ...

  4. mac上传文件到七牛云,使用qshell上传文件到七牛云

    第一步:下载qshell(其实就是命令行,不是什么直接能打开的文件,也不是exe文件) 地址:https://developer.qiniu.com/kodo/tools/1302/qshell 选择 ...

  5. jsp使用七牛云API和webuploader上传多组图片

    文章目录 jsp使用七牛云API和webuploader上传多组图片 介绍 目录树 遇到的问题 上传组件的选择问题 进度条 多线程 前端界面 数据库 关键代码 UserPhotoDaoImpl Upl ...

  6. 腾讯 COS 对象存储 - SDK 文件上传对接,示例代码

    文章目录 腾讯 COS 对象存储 - SDK 文件上传对接,示例代码 1.SDK 2.工具类代码 3.使用示例 腾讯 COS 对象存储 - SDK 文件上传对接,示例代码 1.SDK <depe ...

  7. SpringBoot整合腾讯云COS对象存储实现文件上传

    企业级项目开发中都会有文件.图片.视频等文件上传并能够访问的场景,对于初学者Demo可能会直接存储在应用服务器上:对于传统项目可能会单独搭建FastDFS.MinIO等文件服务来实现存储,这种方案可能 ...

  8. java上传视频到七牛云_使用SpringMVC上传视频到七牛云上

    最近做项目用到了七牛云存储,就讲一下如何使用springMVC对视频上传到七牛云上. Java SDK依赖的第三方库及其版本如下: 这个是使用maven管理的依赖. 也可以自己手动下载jar包通过这个 ...

  9. 七牛云入门及图片上传

    什么是七牛云? 七牛云是国内领先的企业级公有云服务商,致力于打造以数据为核心的场景化PaaS服务.围绕富媒体场景,七牛先后推出了对象存储,融合CDN加速,数据通用处理,内容反垃圾服务,以及直播云服务等 ...

  10. 腾讯云cos对象存储服务文件上传api就是一个大坑

    一.介绍 对象存储服务(Cloud Object Service)是基于腾讯多年海量服务经验,对外提供的可靠.安全.易用的海量存储服务.提供多样化接入方式,以及全国部署的上传加速集群,可以无缝衔接CD ...

最新文章

  1. ValueError: fill value must be in categories.
  2. mysql 存储过程 互斥_mysql中的存储过程和事务隔离
  3. 用bat实现自启动程序
  4. 【广东省】2020年下软考证书领取通知
  5. volatile关键字及JMM模型
  6. java中toarray()的 用法_java容器中toArray的用法
  7. 20155238 实验四 Android程序设计
  8. 非常有趣的古越及吴语-台州话
  9. Polly 重试策略
  10. 【解析】案例4-1.5 顺序存储的二叉树的最近公共祖先问题
  11. Smart Template component rendering process - part 2
  12. 解压版本的tomcat服务安装
  13. node搭建服务器,写接口,调接口,跨域
  14. php 加密保存mysql_PHP及MYSQL中字符串加密函数
  15. Oracle事务处理
  16. 鸿蒙系统公布名单,鸿蒙系统首批升级名单公布_鸿蒙系统首批升级机型
  17. MTK 驱动(81)----不开机问题快速分析
  18. 双十一,不玩盖楼,直接大额降价!
  19. Oracle项目管理系统之合同台账
  20. overflow:atuo;隐藏滚动条

热门文章

  1. android 跳转京东app,第三方应用跳转到京东app
  2. java模板引擎哪个好_模板引擎比较
  3. java将模糊的图片变清晰_java高清图生成缩略图变得模糊
  4. Java使用 PDFBox 从 pdf 中提取图像
  5. 动态规划之背包问题——背包三讲(01背包,完全背包,多重背包)
  6. oracle检查表失效,PL/SQL联系oracle成功可以sql解决的办法是检查表的名称无法显示...
  7. 某一api全开源网站源码
  8. 微信群解答_各月各旬降水量均值_巧用groupby
  9. 89c51 单片机 c语言 编写sszymmh 歌曲 文档,Proteus仿真51单片机生日快乐音乐播放器...
  10. 如何做好科研实验记录