Files
Mir4UpdaterBackend/src/main/java/com/mir4updater/backend/controller/apk/CreateDiffPatchFileController.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("未知错误");
}
}