222 lines
11 KiB
Java
222 lines
11 KiB
Java
package com.mir4updater.backend.controller.apk;
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.mir4updater.backend.config.FilePath;
|
|
import com.mir4updater.backend.entity.ApkInfo;
|
|
import com.mir4updater.backend.entity.XapkManifest;
|
|
import com.mir4updater.backend.result.Result;
|
|
import com.mir4updater.backend.service.ApkInfoService;
|
|
import com.mir4updater.backend.utils.ApkUtils;
|
|
import com.mir4updater.backend.utils.HashUtils;
|
|
import com.mir4updater.backend.utils.PatchUtils;
|
|
import net.dongliu.apk.parser.ApkFile;
|
|
import net.dongliu.apk.parser.bean.ApkMeta;
|
|
import net.dongliu.apk.parser.bean.UseFeature;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.apache.tomcat.util.http.fileupload.FileUploadException;
|
|
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 org.springframework.web.multipart.MultipartFile;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.*;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipFile;
|
|
|
|
@RestController
|
|
public class CreateDiffPatchFileController {
|
|
public static Logger logger = LogManager.getLogger(CreateDiffPatchFileController.class);
|
|
|
|
@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 + FilePath.UPLOAD_PATH + File.separator + FilePath.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");
|
|
long startTime = System.currentTimeMillis();
|
|
logger.info("started at " + startTime);
|
|
PatchUtils.BsdiffResult bsdiffResult = PatchUtils.createDiffPatch(oldApkInfo.getFilePath(), newApkInfo.getFilePath(), patchFile.getPath());
|
|
long endTime = System.currentTimeMillis() - startTime;
|
|
int exitCode = bsdiffResult.getExitCode();
|
|
String result = bsdiffResult.getResult();
|
|
|
|
if (exitCode == 0) {
|
|
String md5 = HashUtils.getFileMD5(patchFile);
|
|
logger.info("patchFile md5 = " + md5 + " 用时:" + endTime + "ms");
|
|
return Result.success(md5);
|
|
} else {
|
|
return Result.error(result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 通过上传文件查询最新版本并创建最新补丁文件
|
|
*
|
|
* @param multipartFile
|
|
* @return
|
|
* @throws Exception
|
|
*/
|
|
@PostMapping("/android/create_latest_patch")
|
|
public Result createLatestPatch(@RequestParam("file") MultipartFile multipartFile) throws Exception {
|
|
if (multipartFile != null && !multipartFile.isEmpty()) {
|
|
String projectPath = System.getProperty("user.dir");
|
|
logger.info("当前项目路径为:" + projectPath);
|
|
String tempPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.TEMP_PATH + File.separator;
|
|
File tempFileDir = new File(tempPath);
|
|
if (!tempFileDir.exists()) {
|
|
tempFileDir.mkdirs();
|
|
}
|
|
//1.接收上传的应用,写入到硬盘
|
|
String originalFilename = multipartFile.getOriginalFilename();
|
|
File tempFile = new File(tempPath + File.separator + originalFilename);
|
|
logger.info("tempFile path = " + tempFile.getAbsolutePath());
|
|
|
|
try {
|
|
multipartFile.transferTo(tempFile);
|
|
} catch (FileUploadException e) {
|
|
logger.error(e.getMessage());
|
|
}
|
|
|
|
if (!ApkUtils.isAPK(tempFile) && !ApkUtils.isXAPK(tempFile)) {
|
|
return Result.error().setMessage("请上传apk或xapk文件");
|
|
}
|
|
|
|
String md5 = HashUtils.getFileMD5(tempFile);
|
|
logger.info("tempFile md5 = " + md5);
|
|
String sha1 = HashUtils.calculateSHA1(tempFile);
|
|
logger.info("tempFile sha1 = " + sha1);
|
|
String sha256 = HashUtils.calculateSHA256(tempFile);
|
|
logger.info("tempFile sha256 = " + sha256);
|
|
|
|
if (ApkUtils.isAPK(tempFile)) {
|
|
logger.info("tempFile type is apk");
|
|
|
|
ApkFile apkFile = new ApkFile(tempFile);
|
|
ApkMeta apkMeta = apkFile.getApkMeta();
|
|
apkFile.close();
|
|
|
|
logger.info(apkMeta.getLabel());
|
|
logger.info(apkMeta.getPackageName());
|
|
logger.info(apkMeta.getVersionCode());
|
|
logger.info(apkMeta.getVersionName());
|
|
for (UseFeature feature : apkMeta.getUsesFeatures()) {
|
|
logger.info(feature.getName());
|
|
}
|
|
|
|
String packageName = apkMeta.getPackageName();
|
|
long versionCode = apkMeta.getVersionCode();
|
|
|
|
List<ApkInfo> apkInfoList = apkInfoService.getApkInfo(packageName);
|
|
Optional<ApkInfo> apkInfoOptional = apkInfoList.stream().max(new Comparator<ApkInfo>() {
|
|
@Override
|
|
public int compare(ApkInfo o1, ApkInfo o2) {
|
|
return Long.compare(o1.getVersionCode(), o2.getVersionCode());
|
|
}
|
|
});
|
|
if (apkInfoOptional.isPresent()) {
|
|
ApkInfo apkInfo = apkInfoOptional.get();
|
|
logger.info("latest apkinfo = " + apkInfo);
|
|
if (versionCode >= apkInfo.getVersionCode()) {
|
|
return Result.success().setMessage(packageName + " 已是最新版本");
|
|
} else {
|
|
String apkPath = apkInfo.getFilePath();
|
|
String patchPath = projectPath + File.separator + FilePath.UPLOAD_PATH + File.separator + FilePath.PATCH_PATH + File.separator;
|
|
File patchFile = new File(patchPath + packageName + "_" + versionCode + "_" + apkInfo.getVersionCode() + ".patch");
|
|
long startTime = System.currentTimeMillis();
|
|
logger.info("started at " + startTime);
|
|
PatchUtils.BsdiffResult bsdiffResult = PatchUtils.createDiffPatch(tempFile.getAbsolutePath(), apkPath, patchFile.getPath());
|
|
long endTime = System.currentTimeMillis() - startTime;
|
|
|
|
int exitCode = bsdiffResult.getExitCode();
|
|
String result = bsdiffResult.getResult();
|
|
|
|
if (exitCode == 0) {
|
|
String patchMd5 = HashUtils.getFileMD5(patchFile);
|
|
logger.info("patchFile md5 = " + patchMd5);
|
|
logger.info("tempFile delete = " + tempFile.delete());
|
|
Map<String, Object> map = new HashMap<>();
|
|
map.put("patchMd5", patchMd5);
|
|
map.put("path", patchFile.getAbsolutePath());
|
|
map.put("time", endTime + "ms");
|
|
return Result.mapResult(map).setSuccess(true).setCode(Result.CODE_SUCCESS).setMessage("创建成功");
|
|
} else {
|
|
logger.info("tempFile delete = " + tempFile.delete());
|
|
return Result.error(result);
|
|
}
|
|
}
|
|
} else {
|
|
logger.info("tempFile delete = " + tempFile.delete());
|
|
return Result.error().setMessage(packageName + " 没有上传其他版本");
|
|
}
|
|
} else if (ApkUtils.isXAPK(tempFile)) {
|
|
logger.info("tempFile type is xapk");
|
|
try (ZipFile zipFile = new ZipFile(tempFile)) {
|
|
// 遍历ZIP文件中的所有条目
|
|
zipFile.stream().forEach(zipEntry -> {
|
|
logger.info(zipEntry.getName());
|
|
});
|
|
Optional<? extends ZipEntry> manifestOptional = zipFile.stream().filter(zipEntry -> "manifest.json".equals(zipEntry.getName())).findFirst();
|
|
if (manifestOptional.isPresent()) {
|
|
ZipEntry manifestZipEntry = manifestOptional.get();
|
|
try {
|
|
InputStream manifestInputStream = zipFile.getInputStream(manifestZipEntry);
|
|
// 读取文件内容
|
|
String content = new String(manifestInputStream.readAllBytes(), StandardCharsets.UTF_8);
|
|
// 创建ObjectMapper实例
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
// 将Java对象转换为JSON字符串
|
|
String jsonString = objectMapper.writeValueAsString(content);
|
|
XapkManifest xapkManifest = objectMapper.readValue(content, XapkManifest.class);
|
|
// logger.info(xapkManifest);
|
|
logger.info(xapkManifest.getName());
|
|
logger.info(xapkManifest.getPackage_name());
|
|
logger.info(xapkManifest.getVersion_code());
|
|
logger.info(xapkManifest.getVersion_name());
|
|
|
|
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
System.err.println("读取文件失败: " + manifestZipEntry.getName());
|
|
return Result.error().setMessage("读取文件失败");
|
|
}
|
|
} else {
|
|
return Result.error().setMessage("读取xapk配置失败");
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
System.err.println("打开ZIP文件失败: " + e.getMessage());
|
|
return Result.error().setMessage("打开ZIP文件失败");
|
|
}
|
|
}
|
|
|
|
} else {
|
|
return Result.error().setMessage("文件为空");
|
|
}
|
|
return Result.error().setMessage("未知错误");
|
|
}
|
|
}
|