前几天在做项目时:遇到七牛图片上传,一次9张上传,但是七牛目前没有提供这种上传方式,那么怎么处理,难不成我通过for循环上传,这样有点不切合项目的需求,容易出错,想了很多办法,想起rxjava2.0可以实现list集合便利,发送数据到下游observable.那么能使用rxjava实现图片上传吗?答案是:可以的.
具体实现思路如下:
- 项目要求图片是有序的,用户的选择就是上传顺序,也就是图片返回路径的顺序
- 使用rxjava2.0的fromIterable这个操作符才给下游发送数据,使用concatMap来保证有序
思路有了,改怎么处理,也絮叨很多错误,在下面会有说明:
- 七牛图片中不能有中文,不能有特殊符号,且图片的类型不能是" image/* "(可以是null),这样都会引起图片上传失败
- 如果想实现图片覆盖上传,最好是启用七牛的策略上传模式
- 七牛图片上传的token有效时长和官方文档保持一致,也就是未来30m-60m,不然会有token失效的异常.
- 如果才用的是非策略模式上传会提示"file exist",不会提示文件上传成功.
以上是我在开发中遇到的错误具体该怎么规避,我会贴出相关代码,
下面的代码是讲文件的中文名字改正拼音,不过我建议直接使用md5,不用再引入拼音库.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.github.promeg.pinyinhelper.Pinyin.isChinese;
import static com.github.promeg.pinyinhelper.Pinyin.toPinyin;
public class HanZiUtils {
public static boolean isContainChinese(String str) {
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
Matcher m = p.matcher(str);
if (m.find()) {
return true;
}
return false;
}
public static String filterChinese(String str) {
// 用于返回结果
String result = str;
boolean flag = isContainChinese(str);
if (flag) {// 包含中文
// 用于拼接过滤中文后的字符
StringBuffer sb = new StringBuffer();
// 用于校验是否为中文
boolean flag2 = false;
// 用于临时存储单字符
char chinese = 0;
// 5.去除掉文件名中的中文
// 将字符串转换成char[]
char[] charArray = str.toCharArray();
// 过滤到中文及中文字符
for (int i = 0; i < charArray.length; i++) {
chinese = charArray[i];
flag2 = isChinese(chinese);
if (flag2) {// 不是中日韩文字及标点符号
sb.append(toPinyin(chinese).toLowerCase());
}else{
sb.append(chinese);
}
}
result = sb.toString();
}
return result;
}
}
在用户选择完图片后使用上面的工具类判断调用方法如下:
String name = HanZiUtils.filterChinese(name);//把中文名称转换成拼音
/**
* 七牛下载的路径
*/
public static final String DOWN_LOAD_URL = "";
/**
* 七牛空间域名
*/
public static final String BUKET_NAME = "";
这样就把中文名称改成拼音了.然后保存.
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import com.kalading.engineers.BuildConfig;
import com.kaopiz.kprogresshud.KProgressHUD;
import com.qiniu.android.common.Zone;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.Configuration;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.utils.UrlSafeBase64;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* 七牛上传工具类
*/
public class QiniuUploadUtils {
//七牛后台的key
private static String AccessKey = "";
//七牛后台的secret
private static String SecretKey = "";
private static final String MAC_NAME = "HmacSHA1";
private static final String ENCODING = "UTF-8";
/**
* 七牛云SDK
*/
public static Configuration configuration = new Configuration.Builder()
//.chunkSize(512 * 1024) // 分片上传时,每片的大小。 默认256K
//.putThreshhold(1024 * 1024) // 启用分片上传阀值。默认512K
.connectTimeout(60) // 链接超时。默认10秒
//.useHttps(true) // 是否使用https上传域名
.responseTimeout(60) // 服务器响应超时。默认60秒
//.recorder(recorder) // recorder分片上传时,已上传片记录器。默认null
//.recorder(recorder, keyGen) // keyGen 分片上传时,生成标识符,用于片记录器区分是那个文件的上传记录
.zone(Zone.zone0) // 设置区域,指定不同区域的上传域名、备用域名、备用IP。
.retryMax(3)
//.dns(null)
.build();
private static UploadManager uploadManager = new UploadManager(configuration);
//unix时间戳:2065-12-31 00:00:00
private static long delayTimes = (System.currentTimeMillis()) / 1000 + 3600L;
public interface QiNiuCallback {
/**
* 上传完成
*/
void onSuccess(String picUrls);
/**
* 上传失败
*/
void onError(String msg, ResponseInfo info);
}
public interface QiNiuCallbackImages {
void onComplete();
/**
* 上传完成
*/
void onSuccess(List<String> picUrls);
/**
* 上传失败
*/
void onError(String msg);
}
/**
* 上传
*
* @param domain bucketName的名字
* @param path 上传文件的路径地址
*/
public static void uploadPic(final String domain, final String path, final String keys, String saveKey,
final QiNiuCallback callBack) {
try {
// 1:第一种方式 构造上传策略
JSONObject json = new JSONObject();
json.put("deadline", delayTimes);// 有效时间为一个小时
json.put("scope", domain);
json.put("saveKey", saveKey);
json.put("mimeLimit", "image/*");
String encodeToString = UrlSafeBase64.encodeToString(json.toString().getBytes());
byte[] sign = HmacSHA1Encrypt(encodeToString, SecretKey);
String encodedSign = UrlSafeBase64.encodeToString(sign);
final String uploadToken = AccessKey + ':' + encodedSign + ':'
+ encodeToString;
uploadManager.put(path, keys, uploadToken,
(key, info, response) -> {
if (info.isOK()) {
String urls = getFileUrl(domain, keys);
callBack.onSuccess(urls);
} else
callBack.onError(key, info);
}, null);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void uploadPicList(final String domain, final String path, final String keys, String saveKey,
final QiNiuCallback callBack) {
try {
// 1:第一种方式 构造上传策略
JSONObject json = new JSONObject();
json.put("deadline", delayTimes);// 有效时间为一个小时
json.put("scope", domain);
json.put("saveKey", saveKey);
//json.put("mimeLimit", "image/*");
String encodeToString = UrlSafeBase64.encodeToString(json.toString().getBytes());
byte[] sign = HmacSHA1Encrypt(encodeToString, SecretKey);
String encodedSign = UrlSafeBase64.encodeToString(sign);
final String uploadToken = AccessKey + ':' + encodedSign + ':'
+ encodeToString;
uploadManager.put(path, keys, uploadToken,
new UpCompletionHandler() {
@Override
public void complete(String key, ResponseInfo info, JSONObject response) {
if (info.isOK()) {
String urls = getFileUrl(domain, keys);
callBack.onSuccess(urls);
} else
callBack.onError(key, info);
}
}, null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 使用 HMAC-SHA1 签名方法对对encryptText进行签名
*
* @param encryptText 被签名的字符串
* @param encryptKey 密钥
* @return
* @throws Exception
*/
private static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey)
throws Exception {
byte[] data = encryptKey.getBytes(ENCODING);
// 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
// 生成一个指定 Mac 算法 的 Mac 对象
Mac mac = Mac.getInstance(MAC_NAME);
// 用给定密钥初始化 Mac 对象
mac.init(secretKey);
byte[] text = encryptText.getBytes(ENCODING);
// 完成 Mac 操作
return mac.doFinal(text);
}
/**
* 通过key获取上传的资源文件的全路径
*
* @param key
* @param domain bucketName的名字
* @return
*/
public static String getFileUrl(String domain, String key) {
StringBuilder sb = new StringBuilder();
sb.append(DOWN_LOAD_URL);
try {
//1:构造URL
String encode = URLEncoder.encode(key, "UTF-8");
sb.append(encode);
//2:为url加上过期时间 unix时间
sb.append("?e=").append(delayTimes);//delayTimes = 1451491200
//3:对1 2 操作后的url进行hmac-sha1签名 secrect
String s = sb.toString();
byte[] bytes = HmacSHA1Encrypt(s, SecretKey);
String sign = UrlSafeBase64.encodeToString(bytes);
//4:将accsesskey 连接起来
sb.append("&token=").append(AccessKey).append(":").append(sign);
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 上传多图
* @param kProgressHUD 弹窗提示
* @param id 唯一标识
* @param images 图片
* @param qiNiuCallback 回调
*/
@SuppressLint("CheckResult")
public static void putImpToQuid(KProgressHUD kProgressHUD, final String id, final List<String> images,
final QiNiuCallbackImages qiNiuCallback) {
// 七牛返回的文件名
final ArrayList<String> resultImagePath = new ArrayList<>();
if (!kProgressHUD.isShowing()) {
kProgressHUD.show();
}
Observable.fromIterable(images)
.concatMap((Function<String, ObservableSource<String>>) path ->
Observable.create((ObservableOnSubscribe<String>) emitter -> {
//int i = PhotoBitmapUtils.readPictureDegree(path);
//Bitmap bitmap = ImageUtil.compressPixel2(path, i + "");
//byte[] bytes = convertBitmap(bitmap);
String[] split = path.split("/");
String key = null;
if (BuildConfig.DEBUG) {
key = "test" + id + "-" + split[split.length - 1];
} else {
key = "release" + id + "-" + split[split.length - 1];
}
//key = filterChinese(key);
JSONObject json = new JSONObject();
json.put("deadline", delayTimes);// 有效时间为一个小时
json.put("scope", KALADING_JISHI);
json.put("saveKey", key);
//json.put("mimeLimit", "image/jpeg");
String encodeToString = UrlSafeBase64.encodeToString(json.toString().getBytes());
byte[] sign = HmacSHA1Encrypt(encodeToString, SecretKey);
String encodedSign = UrlSafeBase64.encodeToString(sign);
final String uploadToken = AccessKey + ':' + encodedSign + ':' + encodeToString;
LogUtils.debugInfo("Ly_002", key);
uploadManager.put(path, key, uploadToken,
(key1, info, response) -> {
try {
if (info.isOK() || info.error.equals("file exists")) {
//String urls = getFileUrl(KALADING_JISHI, key1);
String path1 = null;
try {
path1 = response.getString("key");
} catch (JSONException e) {
path1 = key1;
}
emitter.onNext(path1);
emitter.onComplete();
//LogUtils.debugInfo("Ly_002", urls);
LogUtils.debugInfo("Ly_002", path1);
} else {
emitter.onError(new Throwable(info.error));
}
}catch (Exception e){
emitter.onError(new Throwable(info.error));
}}, null);
}
)).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
resultImagePath.add(s);
// 如果全部完成,调用成功接口
if (resultImagePath.size() == images.size()) {
qiNiuCallback.onSuccess(resultImagePath);
}
}, throwable -> {
LogUtils.warnInfo("===>", throwable.getMessage());
qiNiuCallback.onError(throwable.getMessage());
});
}
}
对于策略相关,官方将了很多,我参考的是在这里https://www.cnblogs.com/china-soft/p/4925683.html?utm_source=tuicool&utm_medium=referral.
到此项目已经开发完成,自测没问题.