http://blog.csdn.net/jspping/article/details/52538440
在做一些用户需求的时候,公司往往需要工程师采集到更多有用的关于用户的个人信息,然后对用户群进行分析,今天我不是来分析这些的,今天我主要是说
腾讯推出的款云产品,那就是对象存储服务COS,这个产品面向所有开发者,新用户都有免费享有10G的使用权,10G可能对于做方案的工程师来说可能是微不
足道的,比如后视镜和车载方案,会常常需要用到视频的存储与云分享,当然这里不是只本地存储哦,我指的是用户在使用方案商的方案的时候,比如他开车
的时候录了一段视频需要分享到某个域,共享给大家看,比如微信,这时候他肯定需要将这个视频存储在一个服务器上,这时候问题来了,如果你们公司够有
钱够任性可以买更好的服务器和配置,那就不需要用到这个COS对象存储产品了,但是就app的和后台的性能来说,这个就很有必要了
比如我目前就在写一个大型的app,初步用户数大概在50万以上,举个简单的小例子,加入今天有2000+个用户同时一起更换用户头像,那这时候对后台的来说
就是影响效率的操作,当然牛逼的服务器配置我们就不再此对比了,那是超大公司的配置,但我们可以将这个样的负荷转移到别的地方,比如COS对象存储,
我们客户端只需要将用户更换头像的图片地址放到后台上传就行了,跟服务器没有任何交互,一但COS存储完毕,我们会收到Response这时候如果是
Success,那我们再向我们自己服务器的后台插入一个COS的头像地址,下次用户重新登录或者登录超时或更换IOS/Android登录,后台返回一个远程COS头
像地址,我们客户端后台获取,轻松完美!当然还有一个小缺陷就是,如果用户是拍照获取头像的话,因为是超清晰的所以比较大,上传可能会异常的慢,而且设置头像的
时候还可能OOM,但这些都是小问题,因为一个头像本来可见范围就那么一点点,我们客户端是可以采取措施的,比如压缩可裁剪,像我就是写了一个裁剪依赖,对较大的
超过1.5MB的头像首先进行压缩,确保它不会失真就得大于30%的压缩率,然后在裁剪,最后把所有用户头像均控制在1MB以内,轻松搞定!
下面开始分享COS的使用和加密签名算法
COS免费10G地址:https://www.qcloud.com/product/cos.html
进入这个链接以后,点击立即使用,随后完成准备工作开始创建 bucket
创建完毕之后,获取API的一些密钥,点击获取API密钥或密钥管理
拿到这些必须的参数以后,就要开始编写校验签名加密程序了,用来访问COS
Android SDK 地址:https://www.qcloud.com/doc/product/227/3391
SDK地址下载下来的Demo是可用的,但是对签名加密那一块压根就没提,甚是恶心,签名都是从自己后台的PHP接口生成返回到客户端的,而且腾讯官方也没有提供相应
的demo,太不负责任了。。。
于是我开始动手写算了,太坑爹了,加密签名需要用
HMAC-SHA1 对拼接参数进行签名,然后签名串需要使用 Base64 编码
官方给出的提示就如下,其他什么都没有了。。。
SignTmp = HMAC-SHA1(SecretKey, orignal)
Sign = Base64(SignTmp.orignal)
其中SecretKey为2.1节获取的项目Secret Key,orignal为2.2节中拼接好的签名串,首先对orignal使用HMAC-SHA1算法进行签名,然后将orignal附加到签名结果的末尾,再进行Base64编码,得到最终的sign。
注:此处使用的是标准的Base64编码,不是urlsafe的Base64编码,请注意。
这里我还是做一下解释吧,要不然大家都看不懂,HMAC-SHA1函数需要两个参数,一个是SecretKey,这个在API密钥里面获得,另一个是original,这个是需要拼接所有
需要用到的参数,比如:
String Original = “a=%s&b=%s&k=%s&e=%s&t=%s&r=%s&f=”;
a: Appid ,可在API密钥中获取
b: 空间名称bucket,比如笔者上图的 “rmtonline”
k: Secret ID ,可在API密钥中获取
e: 签名的有效期,一个UNIX Epoch时间戳,即签名多久的使用期,最长3个月,从年精确到秒
t: 当前时间的UNIX Epoch时间戳,即签名开始生效的日期,也精确到秒,而且e>t,因为到期日必须大于生效日期
r: 随机串,无符号10进制整数,用户需自行生成,最长10位
f: 这个参数的值可以不填
比如:a=200001&b=newbucket&k=AKIDUfLUEUigQiXqm7CVSspKJnuaiIKtxqAv&e=1438669115&t=1436077115&r=11162&f=
下面开始进行编写签名和加密函数
- private static final String MAC_NAME = “HmacSHA1”;
- private static final String ENCODING = “UTF-8”;
- /**
- *
- * @param SecretKey
- * 密钥
- * @param EncryptText
- * 签名串
- * @return
- * @throws Exception
- */
- public static byte[] HmacSHA1Encrypt(String SecretKey, String EncryptText)
- throws Exception {
- byte[] data = SecretKey.getBytes(ENCODING);
- SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
- Mac mac = Mac.getInstance(MAC_NAME);
- mac.init(secretKey);
- byte[] text = EncryptText.getBytes(ENCODING);
- return mac.doFinal(text);
- }
该函数返回一个byte[],SecretKey 为你的secretKey 下面开始拼接original
- public static String getSignOriginal() {
- return String.format(TencentUpload.Original,
- ParamPreference.TENCENT_COS_APPID,
- ParamPreference.TENCENT_COS_BUCKET,
- ParamPreference.TENCENT_COS_SECRET_ID,
- String.valueOf(getFurureLinuxDate()),
- String.valueOf(getLinuxDateSimple()), getRandomTenStr());
- }
TencentUpload.Original 为待通配字符串:String Original = “a=%s&b=%s&k=%s&e=%s&t=%s&r=%s&f=”ParamPreference.TENCENT_COS_APPID 为常量字符串,即你的appid
ParamPreference.TENCENT_COS_BUCKET 为常量字符串,即你的bucket 名称
ParamPreference.TENCENT_COS_SECRET_ID 为常量字符串,即你的 secretID
String.valueOf(getFurureLinuxDate()) 和 String.valueOf(getLinuxDateSimple()) 为签名生效期和到期日的Linux时间戳
getRandomTenStr() 为随即无符号的int 5-8 位 开头不为0 后面为0-9的转字符串拼接
- @SuppressLint(“SimpleDateFormat”)
- public static long getFurureLinuxDate() {
- try {
- String futureTime = ParamPreference.TENCENT_COS_FUTURE_LINUXTIME;
- Date date = new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”)
- .parse(futureTime);
- long unixTimestamp = date.getTime() / 1000L;
- return unixTimestamp;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return –1;
- }
- public static long getLinuxDateSimple() {
- try {
- long unixTimestamp = System.currentTimeMillis() / 1000L;
- return unixTimestamp;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return –1;
- }
- private static String getRandomTenStr() {
- String randomstr = null;
- randomstr = String.valueOf(new Random().nextInt(8) + 1);
- int random = new Random().nextInt(3) + 5;
- for (int i = 0; i < random; i++) {
- randomstr += String.valueOf(new Random().nextInt(9));
- }
- return randomstr;
- }
随后需要将他们签名和加密,最后转成标准Base64编码格式,需要注意的是,获取 SIGN 需要将 secretKey 和 original 传入HmacSHA1Encrypt(String SecretKey, String EncryptText)函数,获取到签名的byte[]结果后,还需要在其后面追加 original 最后再进行Base64编码转码得的 SIGN
编写代码如下:
- public String getTencentSign() {
- try {
- String Original = TencentUtils.getSignOriginal();
- byte[] HmacSHA1 = TencentUtils.HmacSHA1Encrypt(
- ParamPreference.TENCENT_COS_SECRET_KEY, Original);
- byte[] all = new byte[HmacSHA1.length
- + Original.getBytes(ENCODING).length];
- System.arraycopy(HmacSHA1, 0, all, 0, HmacSHA1.length);
- System.arraycopy(Original.getBytes(ENCODING), 0, all,
- HmacSHA1.length, Original.getBytes(ENCODING).length);
- if (DEBUG) {
- Log.v(TencentUpload.TAG, “getTencentSign() Original:\n”
- + Original);
- }
- String SignData = Base64Util.encode(all);
- if (DEBUG) {
- Log.v(TencentUpload.TAG, “getTencentSign() SignData:\n”
- + SignData);
- }
- return SignData;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return “get sign failed”;
- }
ParamPreference.TENCENT_COS_SECRET_KEY 为常量字符串,即你的 secretKeyBase64Util:
- package com.rmt.online.tools;
- import java.io.ByteArrayOutputStream;
- public class Base64Util {
- private static final char[] base64EncodeChars = new char[] { ‘A’, ‘B’, ‘C’,
- ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’,
- ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’, ‘a’, ‘b’, ‘c’,
- ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’,
- ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’, ‘0’, ‘1’, ‘2’,
- ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘+’, ‘/’ };
- private static byte[] base64DecodeChars = new byte[] { –1, –1, –1, –1, –1,
- –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1,
- –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1, –1,
- –1, –1, –1, –1, 62, –1, –1, –1, 63, 52, 53, 54, 55, 56, 57, 58, 59,
- 60, 61, –1, –1, –1, –1, –1, –1, –1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, –1,
- –1, –1, –1, –1, –1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
- 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, –1, –1, –1,
- –1, –1 };
- private Base64Util() {
- }
- public static String encode(byte[] data) {
- StringBuffer sb = new StringBuffer();
- int len = data.length;
- int i = 0;
- int b1, b2, b3;
- while (i < len) {
- b1 = data[i++] & 0xff;
- if (i == len) {
- sb.append(base64EncodeChars[b1 >>> 2]);
- sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
- sb.append(“==”);
- break;
- }
- b2 = data[i++] & 0xff;
- if (i == len) {
- sb.append(base64EncodeChars[b1 >>> 2]);
- sb.append(base64EncodeChars[((b1 & 0x03) << 4)
- | ((b2 & 0xf0) >>> 4)]);
- sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
- sb.append(“=”);
- break;
- }
- b3 = data[i++] & 0xff;
- sb.append(base64EncodeChars[b1 >>> 2]);
- sb.append(base64EncodeChars[((b1 & 0x03) << 4)
- | ((b2 & 0xf0) >>> 4)]);
- sb.append(base64EncodeChars[((b2 & 0x0f) << 2)
- | ((b3 & 0xc0) >>> 6)]);
- sb.append(base64EncodeChars[b3 & 0x3f]);
- }
- return sb.toString();
- }
- public static byte[] decode(String str) {
- byte[] data = str.getBytes();
- int len = data.length;
- ByteArrayOutputStream buf = new ByteArrayOutputStream(len);
- int i = 0;
- int b1, b2, b3, b4;
- while (i < len) { /* b1 */
- do {
- b1 = base64DecodeChars[data[i++]];
- } while (i < len && b1 == –1);
- if (b1 == –1) {
- break;
- } /* b2 */
- do {
- b2 = base64DecodeChars[data[i++]];
- } while (i < len && b2 == –1);
- if (b2 == –1) {
- break;
- }
- buf.write((int) ((b1 << 2) | ((b2 & 0x30) >>> 4))); /* b3 */
- do {
- b3 = data[i++];
- if (b3 == 61) {
- return buf.toByteArray();
- }
- b3 = base64DecodeChars[b3];
- } while (i < len && b3 == –1);
- if (b3 == –1) {
- break;
- }
- buf.write((int) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); /* b4 */
- do {
- b4 = data[i++];
- if (b4 == 61) {
- return buf.toByteArray();
- }
- b4 = base64DecodeChars[b4];
- } while (i < len && b4 == –1);
- if (b4 == –1) {
- break;
- }
- buf.write((int) (((b3 & 0x03) << 6) | b4));
- }
- return buf.toByteArray();
- }
- public static void main(String[] args) throws Exception {
- System.out.println(encode(“liao”.getBytes()));
- System.out.println(new String(decode(encode(“1”.getBytes()))));
- }
- }
最后附上工具类代码:
- package com.rmt.online.tools;
- /**
- * class: TencentUtils
- * author: Engineer-Jsp
- * use: Tencent cos cdn upload and download utils
- * date: 2016/09/14
- * */
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Random;
- import javax.crypto.Mac;
- import javax.crypto.SecretKey;
- import javax.crypto.spec.SecretKeySpec;
- import android.annotation.SuppressLint;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.util.Log;
- import com.rmt.online.preference.ParamPreference;
- import com.tencent.download.Downloader;
- import com.tencent.download.core.DownloadResult;
- import com.tencent.upload.UploadManager;
- import com.tencent.upload.Const.FileType;
- import com.tencent.upload.task.IUploadTaskListener;
- import com.tencent.upload.task.ITask.TaskState;
- import com.tencent.upload.task.data.FileInfo;
- import com.tencent.upload.task.impl.FileUploadTask;
- public class TencentUtils {
- public static boolean DEBUG = true;
- private static final String MAC_NAME = “HmacSHA1”;
- private static final String ENCODING = “UTF-8”;
- /**
- *
- * @param SecretKey
- * 密钥
- * @param EncryptText
- * 签名串
- * @return
- * @throws Exception
- */
- public static byte[] HmacSHA1Encrypt(String SecretKey, String EncryptText)
- throws Exception {
- byte[] data = SecretKey.getBytes(ENCODING);
- SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
- Mac mac = Mac.getInstance(MAC_NAME);
- mac.init(secretKey);
- byte[] text = EncryptText.getBytes(ENCODING);
- return mac.doFinal(text);
- }
- public static long getLinuxDateSimple() {
- try {
- long unixTimestamp = System.currentTimeMillis() / 1000L;
- return unixTimestamp;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return –1;
- }
- @SuppressLint(“SimpleDateFormat”)
- public static long getFurureLinuxDate() {
- try {
- String futureTime = ParamPreference.TENCENT_COS_FUTURE_LINUXTIME;
- Date date = new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”)
- .parse(futureTime);
- long unixTimestamp = date.getTime() / 1000L;
- return unixTimestamp;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return –1;
- }
- private static String getRandomTenStr() {
- String randomstr = null;
- randomstr = String.valueOf(new Random().nextInt(8) + 1);
- int random = new Random().nextInt(3) + 5;
- for (int i = 0; i < random; i++) {
- randomstr += String.valueOf(new Random().nextInt(9));
- }
- return randomstr;
- }
- public static String getSignOriginal() {
- return String.format(TencentUpload.Original,
- ParamPreference.TENCENT_COS_APPID,
- ParamPreference.TENCENT_COS_BUCKET,
- ParamPreference.TENCENT_COS_SECRET_ID,
- String.valueOf(getFurureLinuxDate()),
- String.valueOf(getLinuxDateSimple()), getRandomTenStr());
- }
- public static class TencentUpload {
- public static final String TAG = “TencentUpload”;
- private static UploadManager mFileUploadManager = null;
- private FileUploadTask fileUploadTask = null;
- public static String Original = “a=%s&b=%s&k=%s&e=%s&t=%s&r=%s&f=”;
- private static TencentUpload mTencentUpload = null;
- private static Context mContext = null;
- public static TencentUpload initTencentUpload(Context context) {
- mContext = context;
- if (mTencentUpload == null) {
- mTencentUpload = new TencentUpload();
- }
- if (mFileUploadManager == null) {
- mFileUploadManager = new UploadManager(context,
- ParamPreference.TENCENT_COS_APPID, FileType.File,
- ParamPreference.TENCENT_COS_PERSISTENCEID);
- }
- return mTencentUpload;
- }
- public void statrtUpLoad(String loacFilePath, String filename) {
- if (DEBUG) {
- Log.v(TAG, “DEBUG:” + “\nbucket:”
- + ParamPreference.TENCENT_COS_BUCKET + “\nsrcFilePath:”
- + loacFilePath + “\ndestPath:” + “/” + filename);
- }
- fileUploadTask = new FileUploadTask(
- ParamPreference.TENCENT_COS_BUCKET, loacFilePath, “/”
- + filename, “”, false, new IUploadTaskListener() {
- @Override
- public void onUploadFailed(final int errorCode,
- final String errorMsg) {
- Log.v(TAG, “上传失败 ret:” + errorCode + ” msg:”
- + errorMsg);
- }
- @Override
- public void onUploadProgress(final long totalSize,
- final long sendSize) {
- long p = (long) ((sendSize * 100) / (totalSize * 1.0f));
- Log.v(TAG, “上传中: “ + p + “%”);
- }
- @Override
- public void onUploadStateChange(TaskState arg0) {
- }
- @Override
- public void onUploadSucceed(final FileInfo result) {
- Log.v(TAG, “上传成功,下载路径: “ + result.url);
- if (DEBUG) {
- if (mContext == null) {
- return;
- }
- TencentDownload.initTencentDownload(mContext)
- .startDownLoad(result.url);
- }
- }
- });
- try {
- fileUploadTask.setAuth(getTencentSign());
- mFileUploadManager.upload(fileUploadTask);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public String getTencentSign() {
- try {
- String Original = TencentUtils.getSignOriginal();
- byte[] HmacSHA1 = TencentUtils.HmacSHA1Encrypt(
- ParamPreference.TENCENT_COS_SECRET_KEY, Original);
- byte[] all = new byte[HmacSHA1.length
- + Original.getBytes(ENCODING).length];
- System.arraycopy(HmacSHA1, 0, all, 0, HmacSHA1.length);
- System.arraycopy(Original.getBytes(ENCODING), 0, all,
- HmacSHA1.length, Original.getBytes(ENCODING).length);
- if (DEBUG) {
- Log.v(TencentUpload.TAG, “getTencentSign() Original:\n”
- + Original);
- }
- String SignData = Base64Util.encode(all);
- if (DEBUG) {
- Log.v(TencentUpload.TAG, “getTencentSign() SignData:\n”
- + SignData);
- }
- return SignData;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return “get sign failed”;
- }
- }
- public static class TencentDownload {
- private static final String TAG = “TencentDownload”;
- private static TencentDownload mTencentDownload = null;
- private static Downloader mDownloader = null;
- public static TencentDownload initTencentDownload(Context context) {
- if (mTencentDownload == null) {
- mTencentDownload = new TencentDownload();
- }
- if (mDownloader == null) {
- mDownloader = new Downloader(context,
- ParamPreference.TENCENT_COS_APPID,
- ParamPreference.TENCENT_COS_PERSISTENCEID);
- }
- return mTencentDownload;
- }
- public void startDownLoad(String downloadUrl) {
- mDownloader.download(downloadUrl,
- new Downloader.DownloadListener() {
- @Override
- public void onDownloadSucceed(final String url,
- final DownloadResult result) {
- String file_path = result.getPath();
- Bitmap bmp = decodeSampledBitmap(file_path, 2);
- Log.v(TAG, “下载完成:” + bmp.toString());
- }
- @Override
- public void onDownloadProgress(String url,
- long totalSize, final float progress) {
- long nProgress = (int) (progress * 100);
- Log.i(TAG, “下载进度: “ + nProgress + “%”);
- }
- @Override
- public void onDownloadFailed(String url,
- DownloadResult result) {
- }
- @Override
- public void onDownloadCanceled(String url) {
- }
- });
- }
- }
- public static Bitmap decodeSampledBitmap(String path, int sample) {
- final BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- options.inSampleSize = sample;
- options.inJustDecodeBounds = false;
- return BitmapFactory.decodeFile(path, options);
- }
- }
工具类代码里面附带了上传文件和下载文件的函数,以及打印日志,日志调试开启,将 DEBUG 设为 true 即可,DEBUG 为 true 默认开启下载之前上传成功的哪一个文件,文件上传默认的是覆盖模式,即出现同名的文件执行覆盖操作
工具类函数解析:
new FileUploadTask(ParamPreference.TENCENT_COS_BUCKET, loacFilePath, “/”+ filename, “”, false, new IUploadTaskListener()…);
ParamPreference.TENCENT_COS_BUCKET 为你的 bucket 名称
loacFilePath 为你的需要上传的文件的绝对路径
“/”+ filename 为你需要上传文件的相对路径 如:/test.png
第四个参数为空
第五个为回调接口实例
至此关于腾讯存储对象COS的使用和加密签名校验等就分享完毕了,谢谢大家的观看!