背景
车机座舱开发会有远程OTA的需求,多数国产审计的镜像会选择将升级文件存储到华为云OBS对象存储中,然后车机通过一些列验证下载到进行升级。
由于车机流量都是计量收费,动则上G的系统镜像在使用中尤其需要节省。于是有了再下载过程中取消从OBS中下载的要求,但是通过查询文档,发现华为云给出的demo和文档中并没有给出车机android系统的中通取消下载的方法或者示例。
华为文档:https://support.huaweicloud.com/sdk-android-devg-obs/obs_26_0509.html
工单咨询回复
客服提到的参考(实际没有意义):https://obssdk.obs.cn-north-1.myhuaweicloud.com/apidoc/cn/java/index.html
客服给出的方式是中断下载线程。
代码实现
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;
import com.obs.services.exception.ObsException;
import com.obs.services.model.DownloadFileRequest;
import com.obs.services.model.DownloadFileResult;
import com.obs.services.model.ProgressListener;
import com.obs.services.model.ProgressStatus;
import com.tyw.vb2.ota.callback.IDownLoadCallback;
import com.tyw.vb2.ota.entity.SecretEntiy;
import com.tyw.vb2.ota.util.FileUtils;
import java.io.File;
import java.io.IOException;
/**
* Created by Android Studio.
* Date: 2023/8/15
* Time: 19:53
* description:华为obs下载
*/
public class DownloadHelper {
private static final String TAG = "DownloadHelper";
private static DownloadHelper sInstance;
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
//发送进度
String taskNum = (String) msg.obj;
//can发送进度 ResponseHelper.getInstance().sendDownProgress(taskNum, currentProgress);
Message message = Message.obtain();
message.what = 1;
message.obj = taskNum;
handler.sendMessageDelayed(message, PROGRESS_WAIT_TIME);
}
}
};
public final String LOCAL_DOWNLOAD_PAHT = "/sdcard/";
// Endpoint以北京四为例,其他地区请按实际情况填写。
private String endPoint = "https://obs.cn-north-4.myhuaweicloud.com";
private String bucketname = "桶名";
private String objectname = "对象名";
private String ak = "秘钥";
private String sk = "秘钥";
/**
* 下载进度
*/
private int currentProgress = 0;
private String currentTaskNum = null;
private AsyncTask asyncTask;
private ObsClient obsClient = null;
private DownloadFileRequest request = null;
public static DownloadHelper getInstance() {
if (sInstance == null) {
sInstance = new DownloadHelper();
}
return sInstance;
}
public void downObject(String taskNum, String reqPath, IDownLoadCallback callback) {
Log.i(TAG, taskNum + "-新下载任务--》" + reqPath);
if (currentTaskNum != null) {
Log.w(TAG, "正在下载中,取消新任务");
//正在下载,取消任务
return;
}
this.currentTaskNum = taskNum;
if (!IS_DEBUG) {
SecretEntiy secretEntiy = FileUtils.readSerect();
ak = secretEntiy.getAk();
sk = secretEntiy.getSk();
bucketname = getBucketname(reqPath);
objectname = getObjectName(reqPath);
}
if (TextUtils.isEmpty(ak) || TextUtils.isEmpty(sk)) {
Log.e(TAG, "ak或者sk为空");
ResponseHelper.getInstance().sendkeyError(taskNum, ResponseHelper.obsKeyErrorEnum.AK_SK_NULL);
return;
}
asyncTask = new AsyncTask<Void, Integer, File>() {
@Override
protected File doInBackground(Void... voids) {
File file = downOBS(bucketname, objectname);
return file;
}
@Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
if (file != null && callback != null) {
Log.i(TAG, "文件下载成功-->" + file.getPath());
callback.onSuccess(file);
}
}
@Override
protected void onCancelled() {
Log.i(TAG, "异步下载任务终止");
super.onCancelled();
}
}.execute();
}
/**
* 下载
*
* @param bucketname
* @param objectName 错误码:https://support.huaweicloud.com/usermanual-obs/obs_03_0348.html
* @return
*/
private File downOBS(String bucketname, String objectName) {
File file = null;
if (FileUtils.isFileExist(LOCAL_DOWNLOAD_PAHT + objectName)) {
Log.i(TAG, "删除历史文件--》" + objectName);
FileUtils.deleteFile(LOCAL_DOWNLOAD_PAHT + objectName);
}
currentProgress = 0;
// 创建ObsClient实例
ObsConfiguration config = new ObsConfiguration();
config.setSocketTimeout(60 * 1000);
config.setConnectionTimeout(60 * 1000);
config.setEndPoint(endPoint);
obsClient = new ObsClient(ak, sk, config);
DownloadFileRequest request = new DownloadFileRequest(bucketname, objectName);
// 设置下载对象的本地文件路径
request.setDownloadFile(LOCAL_DOWNLOAD_PAHT + objectName);
// 设置分段下载时的最大并发数
request.setTaskNum(3);
// 设置分段大小为100MB
request.setPartSize(100 * 1024 * 1024);
// 开启断点续传模式
request.setEnableCheckpoint(true);
request.setProgressListener(new ProgressListener() {
@Override
public void progressChanged(ProgressStatus status) {
Log.d(TAG, "obs下载进度-》" + status.getTransferPercentage());
currentProgress = status.getTransferPercentage();
}
});
Message msg = Message.obtain();
msg.what = 1;
msg.obj = currentTaskNum;
handler.sendMessageDelayed(msg, PROGRESS_WAIT_TIME);
try {
// 进行断点续传下载
DownloadFileResult result = obsClient.downloadFile(request);
Log.d(TAG, "华为云Metadata:" + result.getObjectMetadata());
file = new File(LOCAL_DOWNLOAD_PAHT + objectName);
if (file != null) {
ResponseHelper.getInstance().sendDownFinish(currentTaskNum);
}
} catch (ObsException e) {
//can异常上报
file = null;
Log.e(TAG, e.getErrorCode() + "-Error Code==>" + e.getResponseCode());
Log.e(TAG, e.getErrorHostId() + "-Error message-->" + e.getMessage());
if (e.getResponseCode() == -1 && !TextUtils.isEmpty(e.getMessage())) {
//无法解析服务器
Log.e(TAG, "wifi无法访问互联网");
ResponseHelper.getInstance().sendDownError(currentTaskNum, ResponseHelper.DownErrorEnum.NOT_WIFI);
} else if ("SignatureDoesNotMatch".equals(e.getErrorCode())) {
//鉴权秘钥已过期
ResponseHelper.getInstance().sendkeyError(currentTaskNum, ResponseHelper.obsKeyErrorEnum.AK_SK_ERROR);
} else if ("RequestTimeout".equals(e.getErrorCode())) {
//超时 ResponseHelper.getInstance().sendDownError(currentTaskNum, ResponseHelper.DownErrorEnum.REQUEST_TIME_OUT);
}
e.printStackTrace();
} finally {
Log.i(TAG, "DOWN FINALLY");
this.currentTaskNum = null;
handler.removeMessages(1);
try {
if (obsClient != null) {
obsClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
private String getObjectName(String reqParams) {
SecretEntiy secretEntiy = FileUtils.readSerect();
String objectName = reqParams.split(";")[1] + "/" + secretEntiy.getVin() + "-" + secretEntiy.getTboxid();
Log.i(TAG, "需要下载的objectName-->" + objectName);
return objectName;
}
private String getBucketname(String reqParams) {
return reqParams.split(";")[0];
}
/**
* 取消下载
*
* @param taskNum
*/
public void cancle(String taskNum) {
if (!TextUtils.isEmpty(taskNum) && taskNum.equals(currentTaskNum) && obsClient != null) {
Log.w(TAG, "取消下载--》" + taskNum);
handler.removeMessages(1);
if (asyncTask != null) {
asyncTask.cancel(true);
}
this.currentTaskNum = null;
}
}
}
提示
由于项目比较着急,代码是用android已经淘汰的AsyncTask编写的,由于华为给出的异常反馈并没有包含can数据给出的错误反馈,所以异常部分判断的也很不规范,并且代码没有优化。各位看官有更好的代码实现,欢迎回复指正。谢谢!