add bsdiff
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -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
BIN
bsdiff/win/bsdiff.exe
Normal file
Binary file not shown.
BIN
bsdiff/win/bspatch.exe
Normal file
BIN
bsdiff/win/bspatch.exe
Normal file
Binary file not shown.
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user