提交 c976bf41 authored 作者: 祁增奎's avatar 祁增奎

1.Android增加OSS本地进行STS认证

2.增加配置参数以及方法
上级 68b1a2a9
## 0.0.1 ## 1.0.0
* TODO: Describe initial release. * TODO: Describe initial release.
## 1.0.1
* MAINTENANCE: 增加外部控制,使用OSS模式还是普通模式
\ No newline at end of file
...@@ -6,6 +6,7 @@ import android.content.Intent ...@@ -6,6 +6,7 @@ import android.content.Intent
import android.util.Log import android.util.Log
import androidx.annotation.NonNull import androidx.annotation.NonNull
import com.clx.apk_update.DownloadAppUtil.aliDownload import com.clx.apk_update.DownloadAppUtil.aliDownload
import com.clx.apk_update.DownloadAppUtil.downloadApkWithoutSTS
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodCall
...@@ -85,6 +86,36 @@ class ApkUpdatePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHan ...@@ -85,6 +86,36 @@ class ApkUpdatePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHan
} }
"downloadApkWithoutSTS" -> {
val data = call.arguments<Map<String, String>>()
val ak = data?.get("ak")
val sk = data?.get("sk")
val endpoint = data?.get("endpoint")
val bucketName = data?.get("bucketName")
val objectKey = data?.get("objectKey")
val localPath = data?.get("localPath")
val localFileName = data?.get("localFileName")
if (ak == null || sk == null || endpoint == null || bucketName == null || objectKey == null || localPath == null || localFileName == null) {
result.error("error", "data is null", null)
return
}
Log.d(
"InstallAPKPlugin",
"setupMethodChannel: $ak $sk $endpoint $bucketName $objectKey $localPath"
)
downloadApkWithoutSTS(
mContext,
ak,
sk,
endpoint,
bucketName,
objectKey,
localPath,
localFileName,
this
)
}
else -> { else -> {
result.notImplemented() result.notImplemented()
} }
......
...@@ -2,12 +2,14 @@ package com.clx.apk_update ...@@ -2,12 +2,14 @@ package com.clx.apk_update
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import com.alibaba.sdk.android.oss.ClientConfiguration
import com.alibaba.sdk.android.oss.ClientException import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.OSSClient import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.ServiceException import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback import com.alibaba.sdk.android.oss.callback.OSSProgressCallback
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
import com.alibaba.sdk.android.oss.model.ResumableDownloadRequest import com.alibaba.sdk.android.oss.model.ResumableDownloadRequest
...@@ -30,9 +32,33 @@ object DownloadAppUtil { ...@@ -30,9 +32,33 @@ object DownloadAppUtil {
localFileName: String, localFileName: String,
progressCallback: ProgressCallback progressCallback: ProgressCallback
) { ) {
downloadApkFile(context, OSSStsTokenCredentialProvider(ak, sk, token), endpoint, bucketName, objectKey, localPath, localFileName, progressCallback)
}
val credentialProvider: OSSCredentialProvider = OSSStsTokenCredentialProvider(ak, sk, token) fun downloadApkWithoutSTS(
val oss = OSSClient(context, endpoint, credentialProvider) context: Context,
ak: String,
sk: String,
endpoint: String,
bucketName: String,
objectKey: String,
localPath: String,
localFileName: String,
progressCallback: ProgressCallback
) {
// 使用AccessKey和SecretKey创建凭证提供者
downloadApkFile(context, OSSPlainTextAKSKCredentialProvider(ak, sk), endpoint, bucketName, objectKey, localPath, localFileName, progressCallback)
}
private fun downloadApkFile(context: Context,
provider: OSSCredentialProvider,
endpoint: String,
bucketName: String,
objectKey: String,
localPath: String,
localFileName: String,
progressCallback: ProgressCallback) {
val oss = OSSClient(context, endpoint, provider)
// 填写下载到本地文件所在的完整路径。 // 填写下载到本地文件所在的完整路径。
...@@ -77,9 +103,5 @@ object DownloadAppUtil { ...@@ -77,9 +103,5 @@ object DownloadAppUtil {
Log.d(TAG, "aliDownload: task.isCompleted = ${task.isCompleted}") Log.d(TAG, "aliDownload: task.isCompleted = ${task.isCompleted}")
} }
} }
\ No newline at end of file
...@@ -24,7 +24,7 @@ class MethodChannelApkUpdate extends ApkUpdatePlatform { ...@@ -24,7 +24,7 @@ class MethodChannelApkUpdate extends ApkUpdatePlatform {
@override @override
void downloadApk(String ak, String sk, String token, String objectKey, void downloadApk(String ak, String sk, String token, String objectKey,
String endpoint, String bucketName, String localPath,localFileName) { String endpoint, String bucketName, String localPath, localFileName) {
methodChannel.invokeMethod<String>('downloadInstall', { methodChannel.invokeMethod<String>('downloadInstall', {
"ak": ak, "ak": ak,
"sk": sk, "sk": sk,
...@@ -33,7 +33,27 @@ class MethodChannelApkUpdate extends ApkUpdatePlatform { ...@@ -33,7 +33,27 @@ class MethodChannelApkUpdate extends ApkUpdatePlatform {
"endpoint": endpoint, "endpoint": endpoint,
"bucketName": bucketName, "bucketName": bucketName,
"localPath": localPath, "localPath": localPath,
"localFileName":localFileName, "localFileName": localFileName,
});
}
@override
void downloadApkWithoutSTS(
String ak,
String sk,
String objectKey,
String endpoint,
String bucketName,
String localPath,
String localFileName) {
methodChannel.invokeMethod<String>('downloadApkWithoutSTS', {
"ak": ak,
"sk": sk,
"objectKey": objectKey,
"endpoint": endpoint,
"bucketName": bucketName,
"localPath": localPath,
"localFileName": localFileName,
}); });
} }
......
...@@ -32,11 +32,31 @@ abstract class ApkUpdatePlatform extends PlatformInterface { ...@@ -32,11 +32,31 @@ abstract class ApkUpdatePlatform extends PlatformInterface {
} }
//支持断点下载并安装 //支持断点下载并安装
void downloadApk(String ak, String sk, String token, String objectKey, void downloadApk(
String endpoint, String bucketName, String localPath,String localFileName) { String ak,
String sk,
String token,
String objectKey,
String endpoint,
String bucketName,
String localPath,
String localFileName) {
throw UnimplementedError('downloadApk() has not been implemented.'); throw UnimplementedError('downloadApk() has not been implemented.');
} }
// 支持断点下载安装(不通过服务器验证STS)
void downloadApkWithoutSTS(
String ak,
String sk,
String objectKey,
String endpoint,
String bucketName,
String localPath,
String localFileName) {
throw UnimplementedError(
'downloadApkWithoutSTS() has not been implemented.');
}
// 添加下载进度监听 // 添加下载进度监听
Stream<Map<String, Object>> addDownloadListener() { Stream<Map<String, Object>> addDownloadListener() {
throw UnimplementedError('addDownloadListener() has not been implemented.'); throw UnimplementedError('addDownloadListener() has not been implemented.');
......
...@@ -12,20 +12,21 @@ class AppUpgradeFactory { ...@@ -12,20 +12,21 @@ class AppUpgradeFactory {
assert(config.token != null && config.token!.isNotEmpty, "请传递token参数"); assert(config.token != null && config.token!.isNotEmpty, "请传递token参数");
assert(config.productCode != null, "请传递productCode参数"); assert(config.productCode != null, "请传递productCode参数");
DioUpdateUtil.dio.options.headers = { DioUpdateUtil.dio.options.headers = {...config.headers ?? {}};
...config.headers ?? {}
};
// 配置版本服务 // 配置版本服务
VersionService versionService = VersionServiceFactory.create( VersionService versionService = VersionServiceFactory.create(
config: config, config: config,
type: config.type ?? "pm", type: config.type ?? "pm",
); );
// 配置下载服务 // 配置下载服务
DownloadService downloadService = DownloadServiceFactory.create( DownloadService downloadService = config.needSTS == true
baseUrl: config.baseUrl, ? DownloadServiceFactory.create(
token: config.token!, baseUrl: config.baseUrl,
productCode: config.productCode!, token: config.token!,
); productCode: config.productCode!,
)
: DownloadServiceFactory.createWithoutToken(
baseUrl: config.baseUrl, productCode: config.productCode!);
return AppUpgradeControl( return AppUpgradeControl(
versionService: versionService, versionService: versionService,
......
...@@ -32,6 +32,11 @@ class AppUpgradeConfig { ...@@ -32,6 +32,11 @@ class AppUpgradeConfig {
/// 顶部图片 /// 顶部图片
final ImageProvider? topImageProvider; final ImageProvider? topImageProvider;
/// 是否需要请求STS鉴权
final bool? needSTS;
@Deprecated(
'请使用AppUpgradeConfig.msl、AppUpgradeConfig.carrier、msl、AppUpgradeConfig.pm、AppUpgradeConfig.pmOSS代替')
const AppUpgradeConfig({ const AppUpgradeConfig({
required this.baseUrl, required this.baseUrl,
this.onceDay = false, this.onceDay = false,
...@@ -43,5 +48,62 @@ class AppUpgradeConfig { ...@@ -43,5 +48,62 @@ class AppUpgradeConfig {
this.type, this.type,
this.headers, this.headers,
this.topImageProvider, this.topImageProvider,
this.needSTS = true,
}); });
/// 老马上来(建议走这个配置)
const AppUpgradeConfig.msl({
required this.baseUrl,
required this.productNo,
this.onceDay = false,
this.token,
this.productCode,
this.appleId,
this.versionNumber,
this.headers,
this.topImageProvider,
this.needSTS = true,
}) : type = 'msl';
/// 承运(建议走这个配置)
const AppUpgradeConfig.carrier({
required this.baseUrl,
this.onceDay = false,
this.token,
this.productCode,
this.appleId,
this.versionNumber,
this.headers,
this.topImageProvider,
this.needSTS = true,
}) : type = 'carrier',
productNo = null;
/// 项目管理工具(自动配置走OSS还是普通下载,需要Token鉴权)
const AppUpgradeConfig.pm({
required this.baseUrl,
this.onceDay = false,
this.token,
this.productCode,
this.appleId,
this.versionNumber,
this.headers,
this.topImageProvider,
}) : type = 'pm',
productNo = null,
needSTS = true;
/// 项目管理工具(自动配置走OSS还是普通下载,不需要Token鉴权)
const AppUpgradeConfig.pmOSS({
required this.baseUrl,
this.onceDay = false,
this.productCode,
this.appleId,
this.versionNumber,
this.headers,
this.topImageProvider,
}) : type = 'pm',
token = '此Token无效,只为了兼容',
productNo = null,
needSTS = false;
} }
...@@ -18,4 +18,16 @@ class DownloadServiceFactory { ...@@ -18,4 +18,16 @@ class DownloadServiceFactory {
return NormalDownloadImpl(); return NormalDownloadImpl();
} }
} }
static DownloadService createWithoutToken({
required String baseUrl,
required String productCode,
}) {
if (baseUrl.startsWith("https://gateway.")) {
return OSSDownloadImpl.withoutToken(
baseUrl: baseUrl, productCode: productCode);
} else {
return NormalDownloadImpl();
}
}
} }
...@@ -17,13 +17,20 @@ class OSSDownloadImpl implements DownloadService { ...@@ -17,13 +17,20 @@ class OSSDownloadImpl implements DownloadService {
final String baseUrl; final String baseUrl;
final String token; final String token;
final String productCode; final String productCode;
final bool needSTS;
StreamSubscription<Map<String, Object>>? _subscription; StreamSubscription<Map<String, Object>>? _subscription;
OSSDownloadImpl({ OSSDownloadImpl({
required this.baseUrl, required this.baseUrl,
required this.token, required this.token,
required this.productCode, required this.productCode,
}); }) : needSTS = true;
OSSDownloadImpl.withoutToken({
required this.baseUrl,
required this.productCode,
}) : token = "",
needSTS = false;
@override @override
Future<void> download({ Future<void> download({
...@@ -47,17 +54,7 @@ class OSSDownloadImpl implements DownloadService { ...@@ -47,17 +54,7 @@ class OSSDownloadImpl implements DownloadService {
} }
// 删除历史下载文件 // 删除历史下载文件
await deleteFiles(Directory(path)); await deleteFiles(Directory(path));
var ossInfo = await getOssInfo();
if (ossInfo == null) {
onError?.call(Exception("oss信息获取失败,请稍后重新"));
return;
}
// 使用OSS下载
var ak = ossInfo?['onceAccessKeyId'];
var sk = ossInfo?["onceAccessKeySecret"];
var token = ossInfo?["onceSecurityToken"];
var objectKey = getObjectKey(url);
var localPathFileName = getFileName(objectKey);
_subscription = _subscription =
ApkUpdatePlatform.instance.addDownloadListener().listen((event) { ApkUpdatePlatform.instance.addDownloadListener().listen((event) {
var count = event['currentSize'] as num; var count = event['currentSize'] as num;
...@@ -78,15 +75,36 @@ class OSSDownloadImpl implements DownloadService { ...@@ -78,15 +75,36 @@ class OSSDownloadImpl implements DownloadService {
onProgress?.call(progress); // 下载进度 onProgress?.call(progress); // 下载进度
}); });
ApkUpdatePlatform.instance.downloadApk( final objectKey = getObjectKey(url);
ak, if (needSTS) {
sk, var ossInfo = await getOssInfo();
token, if (ossInfo == null) {
objectKey, onError?.call(Exception("oss信息获取失败,请稍后重新"));
Constants.ossEndpoint, return;
getOssBucketName(), }
path, // 使用OSS下载
localPathFileName); var ak = ossInfo?['onceAccessKeyId'];
var sk = ossInfo?["onceAccessKeySecret"];
var token = ossInfo?["onceSecurityToken"];
ApkUpdatePlatform.instance.downloadApk(
ak,
sk,
token,
objectKey,
Constants.ossEndpoint,
getOssBucketName(),
path,
getFileName(objectKey));
} else {
ApkUpdatePlatform.instance.downloadApkWithoutSTS(
'LTAI5t9tqPPGM2g8YE8g899t',
'ENGYeR4r5TQdeYz7C51KBsrwE89g7d',
objectKey,
Constants.ossEndpoint,
getOssBucketName(),
path,
getFileName(objectKey));
}
} catch (e) { } catch (e) {
debugPrint(e.toString()); debugPrint(e.toString());
onError?.call(Exception(e.toString())); onError?.call(Exception(e.toString()));
......
name: apk_update name: apk_update
description: A new Flutter project. description: A new Flutter project.
version: 1.0.0 version: 1.0.1
homepage: homepage:
environment: environment:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论