package com.xuqm.update.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.DigestInputStream; import java.security.MessageDigest; import java.util.HexFormat; import java.util.UUID; @Service public class UpdateAssetService { @Value("${update.upload-dir:/tmp/xuqm-update}") private String uploadDir; @Value("${update.base-url:https://update.dev.xuqinmin.com}") private String baseUrl; public String storeAppPackage(MultipartFile apkFile) throws IOException { if (apkFile == null || apkFile.isEmpty()) { return null; } String filename = UUID.randomUUID() + "_" + apkFile.getOriginalFilename(); Path dir = Paths.get(uploadDir, "apk"); Files.createDirectories(dir); Path dest = dir.resolve(filename); apkFile.transferTo(dest.toFile()); return baseUrl + "/files/apk/" + filename; } public StoredRnBundle storeRnBundle(String appId, String platform, String moduleId, MultipartFile bundle) throws Exception { if (bundle == null || bundle.isEmpty()) { throw new IllegalArgumentException("bundle file is required"); } String filename = moduleId + "." + platform.toLowerCase() + ".bundle"; Path dir = Paths.get(uploadDir, "rn", appId, platform.toLowerCase(), moduleId); Files.createDirectories(dir); Path dest = dir.resolve(filename); String md5 = computeMd5(bundle); bundle.transferTo(dest.toFile()); return new StoredRnBundle(dest.toAbsolutePath().toString(), md5); } private String computeMd5(MultipartFile file) throws Exception { MessageDigest digest = MessageDigest.getInstance("MD5"); try (DigestInputStream dis = new DigestInputStream(file.getInputStream(), digest)) { byte[] buf = new byte[8192]; while (dis.read(buf) != -1) { // read fully } } return HexFormat.of().formatHex(digest.digest()); } public record StoredRnBundle(String bundlePath, String md5) {} }