feat: Java SDK baseUrl 内置默认值;fix: update-service 大文件APK解析、file-service hash去重
这个提交包含在:
父节点
d2dea0c332
当前提交
a8293bb4c4
@ -6,6 +6,7 @@ WORKDIR /workspace
|
|||||||
|
|
||||||
COPY pom.xml ./pom.xml
|
COPY pom.xml ./pom.xml
|
||||||
COPY common ./common
|
COPY common ./common
|
||||||
|
COPY im-sdk ./im-sdk
|
||||||
COPY tenant-service ./tenant-service
|
COPY tenant-service ./tenant-service
|
||||||
COPY im-service ./im-service
|
COPY im-service ./im-service
|
||||||
COPY push-service ./push-service
|
COPY push-service ./push-service
|
||||||
|
|||||||
@ -76,9 +76,15 @@ public class FileStorageService {
|
|||||||
Optional<FileEntity> existing = fileRepository.findByHash(hash);
|
Optional<FileEntity> existing = fileRepository.findByHash(hash);
|
||||||
if (existing.isPresent()) {
|
if (existing.isPresent()) {
|
||||||
FileEntity entity = existing.get();
|
FileEntity entity = existing.get();
|
||||||
entity.setLastAccessedAt(Instant.now());
|
// 检查磁盘文件是否仍然存在,若不存在则重新写入
|
||||||
fileRepository.save(entity);
|
if (Files.exists(Paths.get(entity.getStoragePath()))) {
|
||||||
return toUploadResult(entity);
|
entity.setLastAccessedAt(Instant.now());
|
||||||
|
fileRepository.save(entity);
|
||||||
|
return toUploadResult(entity);
|
||||||
|
}
|
||||||
|
// 文件记录存在但磁盘文件已丢失,删除旧记录后重新上传
|
||||||
|
fileRepository.delete(entity);
|
||||||
|
fileRepository.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
String ext = resolveExtension(originalName, mimeType);
|
String ext = resolveExtension(originalName, mimeType);
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public final class XuqmImServerSdk {
|
|||||||
this.objectMapper = new ObjectMapper()
|
this.objectMapper = new ObjectMapper()
|
||||||
.registerModule(new JavaTimeModule())
|
.registerModule(new JavaTimeModule())
|
||||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
this.baseUrl = trimTrailingSlash(Objects.requireNonNull(builder.baseUrl, "baseUrl"));
|
this.baseUrl = trimTrailingSlash(builder.baseUrl);
|
||||||
this.pushBaseUrl = trimTrailingSlash(builder.pushBaseUrl == null ? builder.baseUrl : builder.pushBaseUrl);
|
this.pushBaseUrl = trimTrailingSlash(builder.pushBaseUrl == null ? builder.baseUrl : builder.pushBaseUrl);
|
||||||
this.updateBaseUrl = trimTrailingSlash(builder.updateBaseUrl == null ? builder.baseUrl : builder.updateBaseUrl);
|
this.updateBaseUrl = trimTrailingSlash(builder.updateBaseUrl == null ? builder.baseUrl : builder.updateBaseUrl);
|
||||||
this.appId = Objects.requireNonNull(builder.appId, "appId");
|
this.appId = Objects.requireNonNull(builder.appId, "appId");
|
||||||
@ -1735,8 +1735,10 @@ public final class XuqmImServerSdk {
|
|||||||
return value.endsWith("/") ? value.substring(0, value.length() - 1) : value;
|
return value.endsWith("/") ? value.substring(0, value.length() - 1) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String DEFAULT_BASE_URL = "https://dev.xuqinmin.com";
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private String baseUrl;
|
private String baseUrl = DEFAULT_BASE_URL;
|
||||||
private String pushBaseUrl;
|
private String pushBaseUrl;
|
||||||
private String updateBaseUrl;
|
private String updateBaseUrl;
|
||||||
private String appId;
|
private String appId;
|
||||||
@ -1776,6 +1778,8 @@ public final class XuqmImServerSdk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public XuqmImServerSdk build() {
|
public XuqmImServerSdk build() {
|
||||||
|
Objects.requireNonNull(this.appId, "appId is required");
|
||||||
|
Objects.requireNonNull(this.appSecret, "appSecret is required");
|
||||||
return new XuqmImServerSdk(this);
|
return new XuqmImServerSdk(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,9 @@ public class UpdateAssetService {
|
|||||||
@Value("${update.base-url:https://update.dev.xuqinmin.com}")
|
@Value("${update.base-url:https://update.dev.xuqinmin.com}")
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
|
|
||||||
|
@Value("${file.service.internal-url:http://127.0.0.1:8086}")
|
||||||
|
private String fileServiceInternalUrl;
|
||||||
|
|
||||||
public UpdateAssetService(ObjectMapper objectMapper) {
|
public UpdateAssetService(ObjectMapper objectMapper) {
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
@ -180,9 +183,31 @@ public class UpdateAssetService {
|
|||||||
if (normalized.endsWith(".ipa")) {
|
if (normalized.endsWith(".ipa")) {
|
||||||
return inspectIpa(file, fileName);
|
return inspectIpa(file, fileName);
|
||||||
}
|
}
|
||||||
|
// 文件名无后缀时,按文件魔数尝试解析(APK/IPA 本质都是 ZIP)
|
||||||
|
if (isZipFile(file)) {
|
||||||
|
try {
|
||||||
|
return inspectApk(file, fileName);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return inspectIpa(file, fileName);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
return new AppPackageInspectResult(platformFromFileName(fileName), null, null, null, fileName, false);
|
return new AppPackageInspectResult(platformFromFileName(fileName), null, null, null, fileName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isZipFile(Path file) throws IOException {
|
||||||
|
if (!Files.exists(file) || Files.size(file) < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try (InputStream in = Files.newInputStream(file)) {
|
||||||
|
byte[] magic = new byte[4];
|
||||||
|
int read = in.read(magic);
|
||||||
|
return read == 4 && magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x03 && magic[3] == 0x04;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private AppPackageInspectResult inspectIpa(Path file, String fileName) throws Exception {
|
private AppPackageInspectResult inspectIpa(Path file, String fileName) throws Exception {
|
||||||
try (ZipFile zipFile = new ZipFile(file.toFile())) {
|
try (ZipFile zipFile = new ZipFile(file.toFile())) {
|
||||||
ZipEntry entry = zipFile.stream()
|
ZipEntry entry = zipFile.stream()
|
||||||
@ -338,9 +363,14 @@ public class UpdateAssetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RemotePackage downloadRemotePackage(String packageUrl, boolean tempFile) throws IOException {
|
private RemotePackage downloadRemotePackage(String packageUrl, boolean tempFile) throws IOException {
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URL(packageUrl).openConnection();
|
// 同一台服务器上的 file-service 使用内部地址,避免 Nginx HTTPS 代理的性能损耗
|
||||||
|
String internalUrl = packageUrl;
|
||||||
|
if (packageUrl != null && packageUrl.contains("file.dev.xuqinmin.com")) {
|
||||||
|
internalUrl = packageUrl.replace("https://file.dev.xuqinmin.com", fileServiceInternalUrl);
|
||||||
|
}
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) new URL(internalUrl).openConnection();
|
||||||
connection.setConnectTimeout(15_000);
|
connection.setConnectTimeout(15_000);
|
||||||
connection.setReadTimeout(30_000);
|
connection.setReadTimeout(300_000);
|
||||||
connection.setInstanceFollowRedirects(true);
|
connection.setInstanceFollowRedirects(true);
|
||||||
|
|
||||||
int status = connection.getResponseCode();
|
int status = connection.getResponseCode();
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户