添加FTP备份目标保存路径设置。

This commit is contained in:
2026-01-31 21:03:39 +08:00
parent 530316b976
commit dae39b43d6
4 changed files with 123 additions and 61 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sat Jan 31 12:06:15 GMT 2026
#Sat Jan 31 13:02:06 GMT 2026
stageCount=12
libraryProject=libappbase
baseVersion=15.15
publishVersion=15.15.11
buildCount=27
buildCount=29
baseBetaVersion=15.15.12

View File

@@ -168,18 +168,18 @@ public class MainActivity extends Activity {
public void onFTPBackupsActivity(View view) {
LogUtils.d(TAG, "onFTPBackupsActivity() 调用");
SFTPBackupsSettingsDialog dlg = new SFTPBackupsSettingsDialog(this);
SFTPAuthModel authModel = dlg.getSFTPAuthModelFromSP(this);
SFTPAuthModel authModel = SFTPBackupsSettingsDialog.getSFTPAuthModelFromSP(this);
if (authModel == null) {
dlg.show();
} else {
// 1. 构建应用Data目录待备份文件Map
HashMap<String, String> dataFileMap = new HashMap<>();
// 存入文件key=唯一标识value=应用外部文件目录下的相对路径与原addSdcardFile参数一致
// 存入文件key=唯一标识value=应用Data目录下的相对路径
dataFileMap.put(TestBean.class.getName() + ".json",
getTestBeanRelativePath());
// 1. 构建SDCard目录待备份文件Map与BackupUtils的SdcardMap泛型一致String-String
// 构建SDCard目录待备份文件Map与BackupUtils的SdcardMap泛型一致String-String
HashMap<String, String> sdcardFileMap = new HashMap<>();
// 存入文件key=唯一标识value=应用外部文件目录下的相对路径与原addSdcardFile参数一致
// 存入文件key=唯一标识value=应用专属外部文件目录下的相对路径
sdcardFileMap.put(TestBean.class.getName() + ".json",
getTestBeanRelativePath());
@@ -188,7 +188,8 @@ public class MainActivity extends Activity {
// 3. 序列化传递Map参数使用FTPBackupsActivity中定义的常量避免硬编码
ftpBackupsIntent.putExtra(FTPBackupsActivity.EXTRA_DATA_DIR_FILE_MAP, dataFileMap);
ftpBackupsIntent.putExtra(FTPBackupsActivity.EXTRA_SDCARD_DIR_FILE_MAP, sdcardFileMap);
// 若需要传Data目录的Map同理ftpBackupsIntent.putExtra(FTPBackupsActivity.EXTRA_DATA_DIR_FILE_MAP, dataFileMap);
// 传递FTP上传目标目录参数路径为/WinBoLLStudio/APPBackups/WinBoLL
ftpBackupsIntent.putExtra(FTPBackupsActivity.EXTRA_FTP_TARGET_DIR, "/WinBoLLStudio/APPBackups/WinBoLL");
// 4. 启动Activity参数自动透传
startActivity(ftpBackupsIntent);

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sat Jan 31 12:06:15 GMT 2026
#Sat Jan 31 13:02:06 GMT 2026
stageCount=12
libraryProject=libappbase
baseVersion=15.15
publishVersion=15.15.11
buildCount=27
buildCount=29
baseBetaVersion=15.15.12

View File

@@ -21,18 +21,19 @@ import cc.winboll.studio.libappbase.dialogs.SFTPBackupsSettingsDialog;
/**
* BackupUtils 调用实例
* 支持Intent传入双Map参数初始化BackupUtils的待备份文件列表
* 支持Intent传入双Map参数+FTP上传目标目录初始化BackupUtils的待备份文件列表和上传路径
* 强制校验FTP上传目录参数未传参时直接提示不执行备份
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/30 20:55
* @LastEditTime 2026/02/01 04:00
* @LastEditTime 2026/02/01 11:00
*/
public class FTPBackupsActivity extends Activity {
public static final String TAG = "FTPBackupsActivity";
// 主线程Handler子线程更新UI专用
private Handler mMainHandler;
// FTP服务器上传目标目录(可根据业务自定义
private static final String FTP_TARGET_DIR = "/WinBoLLStudio/APPBackups/WinBoLL/";
// FTP服务器上传目标目录-默认值(移除,改为强制传参,无默认值
private static final String DEFAULT_FTP_TARGET_DIR = null;
// ==================== Intent传参常量规范外部调用====================
/**
@@ -45,10 +46,16 @@ public class FTPBackupsActivity extends Activity {
* 类型HashMap<String,String> 实现Serializable接口
*/
public static final String EXTRA_SDCARD_DIR_FILE_MAP = "extra_sdcard_dir_file_map";
/**
* Intent传入参数-FTP/SFTP服务器上传目标目录【强制传参】
* 类型String 示例:/backup/app/ ,必须传参否则不执行备份
*/
public static final String EXTRA_FTP_TARGET_DIR = "extra_ftp_target_dir";
// 解析后的双Map参数用于初始化BackupUtils
// 解析后的参数用于初始化BackupUtils
private Map<String, String> mDataDirFileMap;
private Map<String, String> mSdcardDirFileMap;
private String mFtpTargetDir; // 解析后的FTP上传目标目录强制非空
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -56,38 +63,60 @@ public class FTPBackupsActivity extends Activity {
setContentView(R.layout.activity_ftp_backups);
// 初始化主线程Handler避免子线程更新UI崩溃
mMainHandler = new Handler(Looper.getMainLooper());
// 解析Intent中的双Map参数
// 解析Intent中的所有参数双Map+FTP上传目录
parseIntentParams();
}
/**
* 解析Intent传入的序列化Map参数
* 兜底处理:参数为空/类型错误时,赋值null兼容BackupUtils初始化逻辑
* 解析Intent传入的所有序列化参数
* 兜底处理:双Map解析失败初始化空MapFTP目录**未传/空则直接赋值null,不兜底默认值**
*/
private void parseIntentParams() {
Intent intent = getIntent();
if (intent != null) {
// 解析Data目录Map强转为HashMap实现Serializable可序列化传递
if (intent == null) {
LogUtils.w(TAG, "parseIntentParamsIntent为null参数解析失败");
initEmptyParams();
return;
}
// 1. 解析Data目录Map强转为HashMap失败则初始化空Map
Serializable dataMapSer = intent.getSerializableExtra(EXTRA_DATA_DIR_FILE_MAP);
if (dataMapSer instanceof HashMap) {
mDataDirFileMap = (HashMap<String, String>) dataMapSer;
LogUtils.d(TAG, "Intent解析Data目录Map成功" + mDataDirFileMap.size() + "个文件");
} else {
mDataDirFileMap = null;
LogUtils.d(TAG, "Intent未解析到有效Data目录Map");
mDataDirFileMap = new HashMap<>();
LogUtils.d(TAG, "Intent未解析到有效Data目录Map初始化空Map");
}
// 解析SDCard目录Map强转为HashMap实现Serializable可序列化传递
// 2. 解析SDCard目录Map强转为HashMap失败则初始化空Map
Serializable sdcardMapSer = intent.getSerializableExtra(EXTRA_SDCARD_DIR_FILE_MAP);
if (sdcardMapSer instanceof HashMap) {
mSdcardDirFileMap = (HashMap<String, String>) sdcardMapSer;
LogUtils.d(TAG, "Intent解析SDCard目录Map成功" + mSdcardDirFileMap.size() + "个文件");
} else {
mSdcardDirFileMap = null;
LogUtils.d(TAG, "Intent未解析到有效SDCard目录Map");
mSdcardDirFileMap = new HashMap<>();
LogUtils.d(TAG, "Intent未解析到有效SDCard目录Map初始化空Map");
}
// 3. 解析FTP上传目标目录**强制校验,未传/空/空格则直接赋值null不兜底**
String inputFtpDir = intent.getStringExtra(EXTRA_FTP_TARGET_DIR);
if (inputFtpDir != null && !inputFtpDir.trim().isEmpty()) {
mFtpTargetDir = inputFtpDir.trim().endsWith("/") ? inputFtpDir.trim() : inputFtpDir.trim() + "/";
LogUtils.d(TAG, "Intent解析FTP上传目录成功" + mFtpTargetDir);
} else {
mFtpTargetDir = null;
LogUtils.w(TAG, "Intent未传递/传递空的FTP上传目录参数禁止执行备份");
}
}
// 未解析到参数时保持null即可BackupUtils内部会默认初始化空Map
/**
* 初始化空参数Intent为null时使用FTP目录直接赋值null
*/
private void initEmptyParams() {
mDataDirFileMap = new HashMap<>();
mSdcardDirFileMap = new HashMap<>();
mFtpTargetDir = null;
}
public void onSFTPSettings(View view) {
@@ -97,57 +126,68 @@ public class FTPBackupsActivity extends Activity {
/**
* 点击事件执行FTP备份核心调用方法
* 主线程仅做UI触发所有核心逻辑在子线程执行
* 每次点击都重新解析Intent参数保证获取最新的文件列表
* 【强制校验】未传递FTP_TARGET_DIR参数时仅吐司提示不执行任何操作
* 每次点击都重新解析Intent参数保证参数最新
*/
public void onBackups(View view) {
ToastUtils.show("开始执行FTP备份请勿退出页面...");
LogUtils.d(TAG, "触发FTP备份操作开启子线程执行核心逻辑");
// 每次点击都重新解析Intent参数避免Activity复用导致参数失效
// 1. 重新解析参数,保证最新
parseIntentParams();
SFTPBackupsSettingsDialog dlg = new SFTPBackupsSettingsDialog(this);
SFTPAuthModel authModel = dlg.getSFTPAuthModelFromSP(this);
if (authModel == null) {
dlg.show();
} else {
// 传入解析后的双Map参数
doBackups(authModel, mDataDirFileMap, mSdcardDirFileMap);
// 2. 强制核心校验FTP上传目录未传参直接吐司提示不执行后续操作
if (mFtpTargetDir == null) {
ToastUtils.show("备份失败未设置FTP上传目标目录参数");
LogUtils.e(TAG, "触发备份操作失败Intent未传递EXTRA_FTP_TARGET_DIR参数");
return;
}
// 3. 校验SFTP配置未配置则弹框
SFTPAuthModel authModel = SFTPBackupsSettingsDialog.getSFTPAuthModelFromSP(this);
if (authModel == null) {
ToastUtils.show("备份失败请先配置SFTP服务器信息");
new SFTPBackupsSettingsDialog(this).show();
LogUtils.w(TAG, "触发备份操作失败未配置SFTP服务器信息");
return;
}
// 4. 所有校验通过,提示并执行备份
ToastUtils.show("开始执行FTP备份请勿退出页面...");
LogUtils.d(TAG, "所有校验通过,开启子线程执行备份核心逻辑");
doBackups(authModel, mDataDirFileMap, mSdcardDirFileMap, mFtpTargetDir);
}
/**
* 核心备份逻辑:子线程执行
* 核心备份逻辑:子线程执行(仅在校验通过后调用)
* @param authModel SFTP认证模型
* @param dataDirFileMap Intent解析的Data目录文件Map
* @param sdcardDirFileMap Intent解析的SDCard目录文件Map
* @param ftpTargetDir Intent解析的FTP上传目标目录已标准化、非空
*/
void doBackups(final SFTPAuthModel authModel, final Map<String, String> dataDirFileMap, final Map<String, String> sdcardDirFileMap) {
void doBackups(final SFTPAuthModel authModel, final Map<String, String> dataDirFileMap,
final Map<String, String> sdcardDirFileMap, final String ftpTargetDir) {
// 所有BackupUtils操作放入子线程规避网络/IO主线程异常
new Thread(new Runnable() {
@Override
public void run() {
try {
// 初始化BackupUtils单例透传解析后的双Map参数
// 初始化BackupUtils单例透传动态解析的FTP上传目录
BackupUtils backupUtils = BackupUtils.getInstance(
getApplicationContext(),
authModel,
FTP_TARGET_DIR,
dataDirFileMap,
sdcardDirFileMap
ftpTargetDir
);
LogUtils.d(TAG, "待备份文件初始化完成 → Data目录" + backupUtils.getAllDataDirFiles().size() + "个 | SDCard目录" + backupUtils.getAllSdcardFiles().size() + "");
// 向BackupUtils添加解析后的待备份文件双Map
addBackupFilesToUtils(backupUtils, dataDirFileMap, sdcardDirFileMap);
LogUtils.d(TAG, "待备份文件初始化完成 → Data目录" + backupUtils.getAllDataDirFiles().size() + "个 | SDCard目录" + backupUtils.getAllSdcardFiles().size() + "个 | 上传目录:" + ftpTargetDir);
// 核心执行打包为ZIP + SFTP上传内部已实现登录→打包→上传→登出
boolean isSuccess = backupUtils.packAndUploadByFtp();
// 主线程反馈执行结果
if (isSuccess) {
updateUi("FTP备份成功文件已打包为ZIP上传至服务器" + FTP_TARGET_DIR);
LogUtils.i(TAG, "FTP备份全流程执行成功");
updateUi("FTP备份成功文件已打包为ZIP上传至服务器" + ftpTargetDir);
LogUtils.i(TAG, "FTP备份全流程执行成功,上传目录:" + ftpTargetDir);
} else {
updateUi("FTP备份失败请检查服务器配置或文件路径");
LogUtils.e(TAG, "FTP备份全流程执行失败");
LogUtils.e(TAG, "FTP备份全流程执行失败,上传目录:" + ftpTargetDir);
}
} catch (IllegalArgumentException e) {
@@ -168,6 +208,26 @@ public class FTPBackupsActivity extends Activity {
}).start();
}
/**
* 向BackupUtils添加待备份文件遍历双Map调用add方法逐个添加
* 先清空原有文件避免Activity复用导致文件重复
* @param backupUtils BackupUtils实例
* @param dataMap Data目录待备份文件Map
* @param sdcardMap SDCard目录待备份文件Map
*/
private void addBackupFilesToUtils(BackupUtils backupUtils, Map<String, String> dataMap, Map<String, String> sdcardMap) {
backupUtils.clearDataDirFiles();
backupUtils.clearSdcardFiles();
// 添加Data目录文件
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
backupUtils.addDataDirFile(entry.getKey(), entry.getValue());
}
// 添加SDCard目录文件
for (Map.Entry<String, String> entry : sdcardMap.entrySet()) {
backupUtils.addSdcardFile(entry.getKey(), entry.getValue());
}
}
/**
* 子线程更新UI工具方法Toast提示
* @param msg 提示信息
@@ -192,9 +252,10 @@ public class FTPBackupsActivity extends Activity {
if (mMainHandler != null) {
mMainHandler.removeCallbacksAndMessages(null);
}
// 清空本地Map引用协助GC不影响BackupUtils内部逻辑
// 清空本地引用协助GC
mDataDirFileMap = null;
mSdcardDirFileMap = null;
mFtpTargetDir = null;
LogUtils.d(TAG, "FTPBackupsActivity销毁已释放本地资源不影响二次备份初始化");
}
}