diff --git a/.gitignore b/.gitignore index e6578fd..c75cc3b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,13 +31,9 @@ build/ ### VS Code ### .vscode/ -/uploadApk/file/config.mdpi.apk -/uploadApk/file/FLYSN-68-V3.2.8-20240829-192522-G6Release.apk -/uploadApk/file/FLYSN-71-V3.3.1-20250307-175850-Huaruian8768Release.apk -/uploadApk/file/MIR4_0.442318_APKPure.xapk -/uploadApk/icon/7110e12ad7dec55cf925b72f266df7ed.png -/uploadApk/icon/268119212d35253700c802830449316a.png -/uploadApk/icon/d016c8c75162d1ae4c3ffbf7a178a111.png +/uploadApk/file/ +/uploadApk/icon/ +/uploadApk/patch/ /hs_err_pid14160.log /hs_err_pid19016.log /hs_err_pid28008.log diff --git a/bsdiff/win/bsdiff.exe b/bsdiff/win/bsdiff.exe new file mode 100644 index 0000000..99ef9c6 Binary files /dev/null and b/bsdiff/win/bsdiff.exe differ diff --git a/bsdiff/win/bspatch.exe b/bsdiff/win/bspatch.exe new file mode 100644 index 0000000..d8f5f70 Binary files /dev/null and b/bsdiff/win/bspatch.exe differ diff --git a/src/main/java/com/mir4updater/backend/controller/apk/CheckUpdateController.java b/src/main/java/com/mir4updater/backend/controller/apk/CheckUpdateController.java index 90d8db6..b7fa35b 100644 --- a/src/main/java/com/mir4updater/backend/controller/apk/CheckUpdateController.java +++ b/src/main/java/com/mir4updater/backend/controller/apk/CheckUpdateController.java @@ -16,7 +16,7 @@ import java.util.Optional; @RestController public class CheckUpdateController { - public static Logger logger = LogManager.getLogger(UploadApkController.class); + public static Logger logger = LogManager.getLogger(CheckUpdateController.class); @Autowired private ApkInfoService apkInfoService; @@ -49,6 +49,10 @@ public class CheckUpdateController { @GetMapping("/android/get_all") public Result getAll() { List apkInfoList = apkInfoService.getAll(); - return Result.success().setData(apkInfoList); + if (apkInfoList==null||apkInfoList.isEmpty()){ + return Result.notFound(); + }else { + return Result.success().setData(apkInfoList); + } } } diff --git a/src/main/java/com/mir4updater/backend/controller/apk/CreateDiffPatchFile.java b/src/main/java/com/mir4updater/backend/controller/apk/CreateDiffPatchFile.java new file mode 100644 index 0000000..0653587 --- /dev/null +++ b/src/main/java/com/mir4updater/backend/controller/apk/CreateDiffPatchFile.java @@ -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 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); + } + } + +} diff --git a/src/main/java/com/mir4updater/backend/controller/apk/UploadApkController.java b/src/main/java/com/mir4updater/backend/controller/apk/UploadApkController.java index 2208fc4..1f2e755 100644 --- a/src/main/java/com/mir4updater/backend/controller/apk/UploadApkController.java +++ b/src/main/java/com/mir4updater/backend/controller/apk/UploadApkController.java @@ -33,32 +33,43 @@ import java.util.zip.ZipFile; @RestController 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); @Autowired 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") public Result uploadApk(@RequestParam("file") MultipartFile multipartFile) throws Exception { if (multipartFile != null && !multipartFile.isEmpty()) { String projectPath = System.getProperty("user.dir"); 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; - File dirFile = new File(dirPath); - if (!dirFile.exists()) { - dirFile.mkdirs(); + String tempPath = projectPath + File.separator + UPLOAD_PATH + File.separator + TEMP_PATH + File.separator; + + File apkFileDir = new File(apkPath); + if (!apkFileDir.exists()) { + apkFileDir.mkdirs(); } - File iconFilePath = new File(iconPath); - if (!iconFilePath.exists()) { - iconFilePath.mkdirs(); + File iconFileDir = new File(iconPath); + if (!iconFileDir.exists()) { + iconFileDir.mkdirs(); } + File tempFileDir = new File(tempPath); + if (!tempFileDir.exists()) { + tempFileDir.mkdirs(); + } + //1.接收上传的应用,写入到硬盘 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()); try { @@ -82,7 +93,7 @@ public class UploadApkController { if (isAPK(file)) { logger.info("file type is apk"); //调用工具类解析apk - //analysisAPK(dirFile.getAbsolutePath(), file.getAbsolutePath()); + //analysisAPK(apkFileDir.getAbsolutePath(), file.getAbsolutePath()); ApkFile apkFile = new ApkFile(file); ApkMeta apkMeta = apkFile.getApkMeta(); @@ -118,9 +129,9 @@ public class UploadApkController { } catch (DataIntegrityViolationException e) { e.printStackTrace(); logger.info(e.getMessage()); - File file1 = new File(file.getAbsolutePath()); - logger.info("delete:" + file1.delete()); return Result.error().setMessage("apk文件无效"); + } finally { + apkFile.close(); } return Result.success().setMessage("上传成功"); @@ -200,15 +211,14 @@ public class UploadApkController { } // 判断是否为APK - public static boolean isAPK(File file) { + public static boolean isAPK(File file) throws IOException { // 检查扩展名是否为.apk if (file.getName().toLowerCase().endsWith(".apk")) { // 进一步验证是否为有效的APK文件 - try (ZipFile zipFile = new ZipFile(file)) { - return zipFile.getEntry("AndroidManifest.xml") != null; - } catch (IOException e) { - return false; - } + ZipFile zipFile = new ZipFile(file); + boolean apkFile = zipFile.getEntry("AndroidManifest.xml") != null; + zipFile.close(); + return apkFile; } return false; } @@ -241,6 +251,7 @@ public class UploadApkController { // hasOBB = true; // } } + zipFile.close(); // XAPK通常包含至少一个APK或OBB数据 return hasAPK || hasJson; } catch (IOException e) { diff --git a/src/main/java/com/mir4updater/backend/controller/apk/UploadPakFileController.java b/src/main/java/com/mir4updater/backend/controller/apk/UploadPakFileController.java index 51b310c..18bc8e0 100644 --- a/src/main/java/com/mir4updater/backend/controller/apk/UploadPakFileController.java +++ b/src/main/java/com/mir4updater/backend/controller/apk/UploadPakFileController.java @@ -20,7 +20,7 @@ import java.util.Collections; @RestController 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"; static { diff --git a/src/main/java/com/mir4updater/backend/repository/ApkInfoRepository.java b/src/main/java/com/mir4updater/backend/repository/ApkInfoRepository.java index 6c0e8ed..f8294ff 100644 --- a/src/main/java/com/mir4updater/backend/repository/ApkInfoRepository.java +++ b/src/main/java/com/mir4updater/backend/repository/ApkInfoRepository.java @@ -9,5 +9,6 @@ import java.util.List; public interface ApkInfoRepository extends JpaRepository { // 自定义查询方法(可选) List findApkInfosByPackageName(String pkg); + ApkInfo findApkInfoByAppId(int id); List findAll(); } diff --git a/src/main/java/com/mir4updater/backend/service/ApkInfoService.java b/src/main/java/com/mir4updater/backend/service/ApkInfoService.java index 4912b09..046903c 100644 --- a/src/main/java/com/mir4updater/backend/service/ApkInfoService.java +++ b/src/main/java/com/mir4updater/backend/service/ApkInfoService.java @@ -20,6 +20,10 @@ public class ApkInfoService { return apkInfoRepository.findApkInfosByPackageName(pkg); } + public ApkInfo getApkInfo(int id) { + return apkInfoRepository.findApkInfoByAppId(id); + } + public List getAll() { return apkInfoRepository.findAll(); }