add bsdiff

This commit is contained in:
2025-04-09 00:22:18 +08:00
parent a123a26274
commit 99b8127934
9 changed files with 143 additions and 30 deletions

10
.gitignore vendored
View File

@@ -31,13 +31,9 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
/uploadApk/file/config.mdpi.apk /uploadApk/file/
/uploadApk/file/FLYSN-68-V3.2.8-20240829-192522-G6Release.apk /uploadApk/icon/
/uploadApk/file/FLYSN-71-V3.3.1-20250307-175850-Huaruian8768Release.apk /uploadApk/patch/
/uploadApk/file/MIR4_0.442318_APKPure.xapk
/uploadApk/icon/7110e12ad7dec55cf925b72f266df7ed.png
/uploadApk/icon/268119212d35253700c802830449316a.png
/uploadApk/icon/d016c8c75162d1ae4c3ffbf7a178a111.png
/hs_err_pid14160.log /hs_err_pid14160.log
/hs_err_pid19016.log /hs_err_pid19016.log
/hs_err_pid28008.log /hs_err_pid28008.log

BIN
bsdiff/win/bsdiff.exe Normal file

Binary file not shown.

BIN
bsdiff/win/bspatch.exe Normal file

Binary file not shown.

View File

@@ -16,7 +16,7 @@ import java.util.Optional;
@RestController @RestController
public class CheckUpdateController { public class CheckUpdateController {
public static Logger logger = LogManager.getLogger(UploadApkController.class); public static Logger logger = LogManager.getLogger(CheckUpdateController.class);
@Autowired @Autowired
private ApkInfoService apkInfoService; private ApkInfoService apkInfoService;
@@ -49,6 +49,10 @@ public class CheckUpdateController {
@GetMapping("/android/get_all") @GetMapping("/android/get_all")
public Result getAll() { public Result getAll() {
List<ApkInfo> apkInfoList = apkInfoService.getAll(); List<ApkInfo> apkInfoList = apkInfoService.getAll();
return Result.success().setData(apkInfoList); if (apkInfoList==null||apkInfoList.isEmpty()){
return Result.notFound();
}else {
return Result.success().setData(apkInfoList);
}
} }
} }

View File

@@ -0,0 +1,97 @@
package com.mir4updater.backend.controller.apk;
import com.mir4updater.backend.entity.ApkInfo;
import com.mir4updater.backend.result.Result;
import com.mir4updater.backend.service.ApkInfoService;
import com.mir4updater.backend.utils.HashUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@RestController
public class CreateDiffPatchFile {
public static Logger logger = LogManager.getLogger(CreateDiffPatchFile.class);
private static final String UPLOAD_PATH = "uploadApk";
public static final String PATCH_PATH = "patch";
@Autowired
private ApkInfoService apkInfoService;
@PostMapping("/android/create_patch")
public Result uploadApk(@RequestParam("old_id") int oldId, @RequestParam("new_id") int newId) throws Exception {
String projectPath = System.getProperty("user.dir");
String patchPath = projectPath + File.separator + UPLOAD_PATH + File.separator + PATCH_PATH + File.separator;
File patchFileDir = new File(patchPath);
if (!patchFileDir.exists()) {
patchFileDir.mkdirs();
}
ApkInfo oldApkInfo = apkInfoService.getApkInfo(oldId);
ApkInfo newApkInfo = apkInfoService.getApkInfo(newId);
if (oldApkInfo == null || newApkInfo == null) {
return Result.notFound();
}
if (!Objects.equals(oldApkInfo.getPackageName(), newApkInfo.getPackageName())) {
return Result.error("包名不一致");
}
if (oldApkInfo.getVersionCode() >= newApkInfo.getVersionCode()) {
return Result.error("旧版本号不能大于等于新版本号");
}
File patchFile = new File(patchPath + oldApkInfo.getPackageName() + "_" + oldApkInfo.getVersionCode() + "_" + newApkInfo.getVersionCode() + ".patch");
// 根据操作系统选择命令
String os = System.getProperty("os.name").toLowerCase();
String command;
if (os.contains("win")) {
command = "bsdiff" + File.separator + "win" + File.separator + "bsdiff.exe";
} else if (os.contains("nix") || os.contains("mac")) {
command = "bsdiff" + File.separator + "linux" + File.separator + "bsdiff";
} else {
throw new UnsupportedOperationException("Unsupported OS");
}
File file = new File(command);
logger.info("bsdiff file exists = " + file.exists());
// 将 command 和 arg 合并成一个数组command 作为第一个参数
List<String> cmdArgs = new ArrayList<>();
cmdArgs.add(command); // 第一个参数是命令本身
cmdArgs.add(oldApkInfo.getFilePath());// 后续参数
cmdArgs.add(newApkInfo.getFilePath());
cmdArgs.add(patchFile.getAbsolutePath());
logger.info("bsdiff args = " + cmdArgs);
ProcessBuilder pb = new ProcessBuilder(cmdArgs); // 传入合并后的参数列表
pb.redirectErrorStream(true); // 合并错误流和输入流
Process process = pb.start();
int exitCode = process.waitFor();
logger.info("退出码: " + exitCode);
// 读取输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String result = reader.lines().collect(Collectors.joining("\n"));
if (exitCode == 0) {
String md5 = HashUtils.getFileMD5(patchFile);
logger.info("patchFile md5 = " + md5);
return Result.success(md5);
} else {
return Result.error(result);
}
}
}

View File

@@ -33,32 +33,43 @@ import java.util.zip.ZipFile;
@RestController @RestController
public class UploadApkController { public class UploadApkController {
private static final String UPLOAD_PATH = "uploadApk";
private static final String APK_FILE_PATH = "file";
private static final String ICON_PATH = "icon";
private static final String TEMP_PATH = "tmp";
public static Logger logger = LogManager.getLogger(UploadApkController.class); public static Logger logger = LogManager.getLogger(UploadApkController.class);
@Autowired @Autowired
private ApkInfoService apkInfoService; private ApkInfoService apkInfoService;
private static final String UPLOAD_PATH = "uploadApk";
private static final String ApkFilePath = "file";
private static final String ICON_PATH = "icon";
@PostMapping("/android/upload_apk") @PostMapping("/android/upload_apk")
public Result uploadApk(@RequestParam("file") MultipartFile multipartFile) throws Exception { public Result uploadApk(@RequestParam("file") MultipartFile multipartFile) throws Exception {
if (multipartFile != null && !multipartFile.isEmpty()) { if (multipartFile != null && !multipartFile.isEmpty()) {
String projectPath = System.getProperty("user.dir"); String projectPath = System.getProperty("user.dir");
logger.info("当前项目路径为:" + projectPath); logger.info("当前项目路径为:" + projectPath);
String dirPath = projectPath + File.separator + UPLOAD_PATH + File.separator + ApkFilePath;
String apkPath = projectPath + File.separator + UPLOAD_PATH + File.separator + APK_FILE_PATH;
String iconPath = projectPath + File.separator + UPLOAD_PATH + File.separator + ICON_PATH + File.separator; String iconPath = projectPath + File.separator + UPLOAD_PATH + File.separator + ICON_PATH + File.separator;
File dirFile = new File(dirPath); String tempPath = projectPath + File.separator + UPLOAD_PATH + File.separator + TEMP_PATH + File.separator;
if (!dirFile.exists()) {
dirFile.mkdirs(); File apkFileDir = new File(apkPath);
if (!apkFileDir.exists()) {
apkFileDir.mkdirs();
} }
File iconFilePath = new File(iconPath); File iconFileDir = new File(iconPath);
if (!iconFilePath.exists()) { if (!iconFileDir.exists()) {
iconFilePath.mkdirs(); iconFileDir.mkdirs();
} }
File tempFileDir = new File(tempPath);
if (!tempFileDir.exists()) {
tempFileDir.mkdirs();
}
//1.接收上传的应用,写入到硬盘 //1.接收上传的应用,写入到硬盘
String originalFilename = multipartFile.getOriginalFilename(); String originalFilename = multipartFile.getOriginalFilename();
File file = new File(dirPath + File.separator + originalFilename); File file = new File(apkPath + File.separator + originalFilename);
logger.info("file path = " + file.getAbsolutePath()); logger.info("file path = " + file.getAbsolutePath());
try { try {
@@ -82,7 +93,7 @@ public class UploadApkController {
if (isAPK(file)) { if (isAPK(file)) {
logger.info("file type is apk"); logger.info("file type is apk");
//调用工具类解析apk //调用工具类解析apk
//analysisAPK(dirFile.getAbsolutePath(), file.getAbsolutePath()); //analysisAPK(apkFileDir.getAbsolutePath(), file.getAbsolutePath());
ApkFile apkFile = new ApkFile(file); ApkFile apkFile = new ApkFile(file);
ApkMeta apkMeta = apkFile.getApkMeta(); ApkMeta apkMeta = apkFile.getApkMeta();
@@ -118,9 +129,9 @@ public class UploadApkController {
} catch (DataIntegrityViolationException e) { } catch (DataIntegrityViolationException e) {
e.printStackTrace(); e.printStackTrace();
logger.info(e.getMessage()); logger.info(e.getMessage());
File file1 = new File(file.getAbsolutePath());
logger.info("delete:" + file1.delete());
return Result.error().setMessage("apk文件无效"); return Result.error().setMessage("apk文件无效");
} finally {
apkFile.close();
} }
return Result.success().setMessage("上传成功"); return Result.success().setMessage("上传成功");
@@ -200,15 +211,14 @@ public class UploadApkController {
} }
// 判断是否为APK // 判断是否为APK
public static boolean isAPK(File file) { public static boolean isAPK(File file) throws IOException {
// 检查扩展名是否为.apk // 检查扩展名是否为.apk
if (file.getName().toLowerCase().endsWith(".apk")) { if (file.getName().toLowerCase().endsWith(".apk")) {
// 进一步验证是否为有效的APK文件 // 进一步验证是否为有效的APK文件
try (ZipFile zipFile = new ZipFile(file)) { ZipFile zipFile = new ZipFile(file);
return zipFile.getEntry("AndroidManifest.xml") != null; boolean apkFile = zipFile.getEntry("AndroidManifest.xml") != null;
} catch (IOException e) { zipFile.close();
return false; return apkFile;
}
} }
return false; return false;
} }
@@ -241,6 +251,7 @@ public class UploadApkController {
// hasOBB = true; // hasOBB = true;
// } // }
} }
zipFile.close();
// XAPK通常包含至少一个APK或OBB数据 // XAPK通常包含至少一个APK或OBB数据
return hasAPK || hasJson; return hasAPK || hasJson;
} catch (IOException e) { } catch (IOException e) {

View File

@@ -20,7 +20,7 @@ import java.util.Collections;
@RestController @RestController
public class UploadPakFileController { public class UploadPakFileController {
public static Logger logger = LogManager.getLogger(UploadApkController.class); public static Logger logger = LogManager.getLogger(UploadPakFileController.class);
private static final String UPLOAD_PATH = "uploadPak"; private static final String UPLOAD_PATH = "uploadPak";
static { static {

View File

@@ -9,5 +9,6 @@ import java.util.List;
public interface ApkInfoRepository extends JpaRepository<ApkInfo, Long> { public interface ApkInfoRepository extends JpaRepository<ApkInfo, Long> {
// 自定义查询方法(可选) // 自定义查询方法(可选)
List<ApkInfo> findApkInfosByPackageName(String pkg); List<ApkInfo> findApkInfosByPackageName(String pkg);
ApkInfo findApkInfoByAppId(int id);
List<ApkInfo> findAll(); List<ApkInfo> findAll();
} }

View File

@@ -20,6 +20,10 @@ public class ApkInfoService {
return apkInfoRepository.findApkInfosByPackageName(pkg); return apkInfoRepository.findApkInfosByPackageName(pkg);
} }
public ApkInfo getApkInfo(int id) {
return apkInfoRepository.findApkInfoByAppId(id);
}
public List<ApkInfo> getAll() { public List<ApkInfo> getAll() {
return apkInfoRepository.findAll(); return apkInfoRepository.findAll();
} }