From b7705df2a4b96b7e32d25cf4c7a2ab8941c5a77e Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 1 Aug 2025 22:39:55 +0800 Subject: [PATCH] Create CacheFileTask --- .../hmcl/ui/versions/DownloadListPage.java | 6 + .../jackhuang/hmcl/task/CacheFileTask.java | 107 ++++++++++++++++++ .../org/jackhuang/hmcl/task/FetchTask.java | 5 +- .../jackhuang/hmcl/task/FileDownloadTask.java | 5 + 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index d2da008cc..ee99f4f85 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -35,6 +35,7 @@ import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; +import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; @@ -44,6 +45,7 @@ import org.jackhuang.hmcl.mod.RemoteMod; import org.jackhuang.hmcl.mod.RemoteModRepository; import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; @@ -61,6 +63,8 @@ import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import java.net.URI; +import java.net.URISyntaxException; import java.util.*; import java.util.stream.Collectors; @@ -68,6 +72,7 @@ import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent; import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class DownloadListPage extends Control implements DecoratorPage, VersionPage.VersionLoadable { protected final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); @@ -540,6 +545,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP .collect(Collectors.toList())); if (StringUtils.isNotBlank(dataItem.getIconUrl())) { + LOG.debug("Icon: " + dataItem.getIconUrl()); imageView.setImage(FXUtils.newRemoteImage(dataItem.getIconUrl(), 40, 40, true, true, true)); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java new file mode 100644 index 000000000..d34d1e3dc --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java @@ -0,0 +1,107 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.task; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +/** + * Download a file to cache repository. + * + * @author Glavo + */ +public class CacheFileTask extends FetchTask { + public CacheFileTask(@NotNull URI uri, int retry) { + super(List.of(uri), retry); + } + + @Override + protected EnumCheckETag shouldCheckETag() { + // Check cache + for (URI uri : uris) { + try { + setResult(repository.getCachedRemoteFile(uri)); + return EnumCheckETag.CACHED; + } catch (IOException ignored) { + } + } + return EnumCheckETag.CHECK_E_TAG; + } + + @Override + protected void useCachedResult(Path cache) { + setResult(cache); + } + + @Override + protected Context getContext(URLConnection connection, boolean checkETag) throws IOException { + assert checkETag; + + Path temp = Files.createTempFile("hmcl-download-", null); + OutputStream fileOutput = Files.newOutputStream(temp); + + return new Context() { + @Override + public void write(byte[] buffer, int offset, int len) throws IOException { + fileOutput.write(buffer, offset, len); + } + + @Override + public void close() throws IOException { + try { + fileOutput.close(); + } catch (IOException e) { + LOG.warning("Failed to close file: " + temp, e); + } + + if (!isSuccess()) { + try { + Files.deleteIfExists(temp); + } catch (IOException e) { + LOG.warning("Failed to delete file: " + temp, e); + } + return; + } + + try { + repository.cacheRemoteFile(connection, temp); + setResult(repository.getCachedRemoteFile(connection.getURL().toURI())); + } catch (URISyntaxException e) { + throw new IOException(e); + } finally { + try { + Files.deleteIfExists(temp); + } catch (IOException e) { + LOG.warning("Failed to delete file: " + temp, e); + } + } + } + }; + } + +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java index b7f27dc72..69bd6e8eb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java @@ -46,7 +46,6 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public abstract class FetchTask extends Task { protected final List uris; protected final int retry; - protected boolean caching; protected CacheRepository repository = CacheRepository.getInstance(); public FetchTask(@NotNull List<@NotNull URI> uris, int retry) { @@ -61,9 +60,7 @@ public abstract class FetchTask extends Task { setExecutor(download()); } - public void setCaching(boolean caching) { - this.caching = caching; - } + public void setCacheRepository(CacheRepository repository) { this.repository = repository; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index ee6224685..35c750146 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -84,6 +84,7 @@ public class FileDownloadTask extends FetchTask { private final Path file; private final IntegrityCheck integrityCheck; + private boolean caching; private Path candidate; private final ArrayList integrityCheckHandlers = new ArrayList<>(); @@ -155,6 +156,10 @@ public class FileDownloadTask extends FetchTask { return file; } + public void setCaching(boolean caching) { + this.caching = caching; + } + public FileDownloadTask setCandidate(Path candidate) { this.candidate = candidate; return this;