mobile download
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package com.goldenchart.service;
|
||||
|
||||
import com.goldenchart.dto.MobileAppReleaseInfoDto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MobileAppReleaseService {
|
||||
|
||||
private static final String DEFAULT_APK_NAME = "goldenchart-android.apk";
|
||||
private static final DateTimeFormatter DT_FMT =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").withZone(ZoneId.of("Asia/Seoul"));
|
||||
|
||||
@Value("${goldenchart.mobile-app.release-dir:data/mobile-releases}")
|
||||
private String releaseDir;
|
||||
|
||||
@Value("${goldenchart.mobile-app.version:}")
|
||||
private String configuredVersion;
|
||||
|
||||
@Value("${goldenchart.mobile-app.public-base-url:}")
|
||||
private String configuredPublicBaseUrl;
|
||||
|
||||
public MobileAppReleaseInfoDto getInfo(String requestBaseUrl) {
|
||||
String publicBaseUrl = resolvePublicBaseUrl(requestBaseUrl);
|
||||
Optional<Path> apk = resolveApkPath();
|
||||
if (apk.isEmpty()) {
|
||||
return MobileAppReleaseInfoDto.builder()
|
||||
.available(false)
|
||||
.installPageUrl(buildInstallPageUrl(publicBaseUrl))
|
||||
.build();
|
||||
}
|
||||
Path file = apk.get();
|
||||
try {
|
||||
long size = Files.size(file);
|
||||
Instant updated = Files.getLastModifiedTime(file).toInstant();
|
||||
String base = normalizeBase(publicBaseUrl);
|
||||
String downloadUrl = base + "/api/mobile-app/download/android";
|
||||
return MobileAppReleaseInfoDto.builder()
|
||||
.available(true)
|
||||
.fileName(file.getFileName().toString())
|
||||
.version(configuredVersion != null && !configuredVersion.isBlank()
|
||||
? configuredVersion
|
||||
: DT_FMT.format(updated))
|
||||
.sizeBytes(size)
|
||||
.updatedAt(DT_FMT.format(updated))
|
||||
.downloadUrl(downloadUrl)
|
||||
.installPageUrl(buildInstallPageUrl(publicBaseUrl))
|
||||
.build();
|
||||
} catch (IOException e) {
|
||||
log.warn("[mobile-app] stat failed: {}", e.getMessage());
|
||||
return MobileAppReleaseInfoDto.builder()
|
||||
.available(false)
|
||||
.installPageUrl(buildInstallPageUrl(publicBaseUrl))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<Resource> downloadAndroid() {
|
||||
Optional<Path> apk = resolveApkPath();
|
||||
if (apk.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
Path file = apk.get();
|
||||
FileSystemResource resource = new FileSystemResource(file);
|
||||
String filename = file.getFileName().toString();
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||
.contentType(MediaType.parseMediaType("application/vnd.android.package-archive"))
|
||||
.body(resource);
|
||||
}
|
||||
|
||||
private Optional<Path> resolveApkPath() {
|
||||
Path dir = Paths.get(releaseDir).toAbsolutePath().normalize();
|
||||
if (!Files.isDirectory(dir)) {
|
||||
log.debug("[mobile-app] release dir missing: {}", dir);
|
||||
return Optional.empty();
|
||||
}
|
||||
Path preferred = dir.resolve(DEFAULT_APK_NAME);
|
||||
if (Files.isRegularFile(preferred)) {
|
||||
return Optional.of(preferred);
|
||||
}
|
||||
try (Stream<Path> stream = Files.list(dir)) {
|
||||
return stream
|
||||
.filter(p -> Files.isRegularFile(p) && p.getFileName().toString().toLowerCase().endsWith(".apk"))
|
||||
.max(Comparator.comparing(p -> {
|
||||
try {
|
||||
return Files.getLastModifiedTime(p).toMillis();
|
||||
} catch (IOException e) {
|
||||
return 0L;
|
||||
}
|
||||
}));
|
||||
} catch (IOException e) {
|
||||
log.warn("[mobile-app] list dir failed: {}", e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildInstallPageUrl(String publicBaseUrl) {
|
||||
return normalizeBase(publicBaseUrl) + "/install.html";
|
||||
}
|
||||
|
||||
private String resolvePublicBaseUrl(String requestBaseUrl) {
|
||||
if (configuredPublicBaseUrl != null && !configuredPublicBaseUrl.isBlank()) {
|
||||
return configuredPublicBaseUrl.trim();
|
||||
}
|
||||
if (requestBaseUrl != null && !requestBaseUrl.isBlank()) {
|
||||
return requestBaseUrl.trim();
|
||||
}
|
||||
return "http://exdev.co.kr";
|
||||
}
|
||||
|
||||
private static String normalizeBase(String publicBaseUrl) {
|
||||
if (publicBaseUrl == null || publicBaseUrl.isBlank()) {
|
||||
return "http://exdev.co.kr";
|
||||
}
|
||||
return publicBaseUrl.replaceAll("/+$", "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user