From a0568e34a8f8ba5a9f1645fce161e694ba730073 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 18 Sep 2025 15:26:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=AE=8B=E7=95=99=E7=9A=84?= =?UTF-8?q?=20java.io.File=20=E7=94=A8=E4=BE=8B=20(#4503)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/java/JavaInstallTask.java | 8 +-- .../java/org/jackhuang/hmcl/ui/FXUtils.java | 32 +++++------ .../hmcl/ui/main/JavaManagementPage.java | 6 +-- .../jackhuang/hmcl/ui/main/SettingsView.java | 2 +- .../jackhuang/hmcl/ui/nbt/NBTEditorPage.java | 2 +- .../jackhuang/hmcl/ui/nbt/NBTFileType.java | 6 ++- .../hmcl/ui/versions/ModListPage.java | 2 +- .../hmcl/ui/versions/SchematicsPage.java | 2 +- .../hmcl/ui/versions/VersionPage.java | 2 +- .../jackhuang/hmcl/ui/versions/Versions.java | 2 +- .../hmcl/ui/versions/WorldListItem.java | 2 +- .../hmcl/ui/versions/WorldManagePage.java | 2 +- .../java/mojang/MojangJavaDownloadTask.java | 5 +- .../hmcl/launch/DefaultLauncher.java | 3 +- .../jackhuang/hmcl/util/CacheRepository.java | 3 +- .../org/jackhuang/hmcl/util/io/FileUtils.java | 53 ++++++++----------- 16 files changed, 67 insertions(+), 65 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInstallTask.java index adb1bd4c2..f6ba84e3b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInstallTask.java @@ -20,10 +20,13 @@ package org.jackhuang.hmcl.java; import kala.compress.archivers.ArchiveEntry; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.IOUtils; import org.jackhuang.hmcl.util.tree.ArchiveFileTree; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -98,8 +101,7 @@ public final class JavaInstallTask extends Task { } if (tree.isExecutable(entry)) - //noinspection ResultOfMethodCallIgnored - path.toFile().setExecutable(true); + FileUtils.setExecutable(path); files.put(String.join("/", nameStack), new JavaLocalFiles.LocalFile(HexFormat.of().formatHex(messageDigest.digest()), size)); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index c56fd334f..df8644085 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -77,12 +77,16 @@ import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.ref.WeakReference; import java.net.*; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; @@ -457,22 +461,27 @@ public final class FXUtils { } } - public static void openFolder(File file) { + public static void openFolder(Path file) { + if (file.getFileSystem() != FileSystems.getDefault()) { + LOG.warning("Cannot open folder as the file system is not supported: " + file); + return; + } + try { - Files.createDirectories(file.toPath()); + Files.createDirectories(file); } catch (IOException e) { LOG.warning("Failed to create directory " + file); return; } - String path = file.getAbsolutePath(); + String path = FileUtils.getAbsolutePath(file); String openCommand; if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) openCommand = "explorer.exe"; else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) openCommand = "/usr/bin/open"; - else if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && new File("/usr/bin/xdg-open").exists()) + else if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && Files.exists(Path.of("/usr/bin/xdg-open"))) openCommand = "/usr/bin/xdg-open"; else openCommand = null; @@ -494,7 +503,7 @@ public final class FXUtils { // Fallback to java.awt.Desktop::open try { - java.awt.Desktop.getDesktop().open(file); + java.awt.Desktop.getDesktop().open(file.toFile()); } catch (Throwable e) { LOG.error("Unable to open " + path + " by java.awt.Desktop.getDesktop()::open", e); } @@ -537,11 +546,11 @@ public final class FXUtils { } // Fallback to open folder - openFolder(file.getParent().toFile()); + openFolder(file.getParent()); }); } else { // We do not have a universal method to show file in file manager. - openFolder(file.getParent().toFile()); + openFolder(file.getParent()); } } @@ -1449,13 +1458,6 @@ public final class FXUtils { return String.format("#%02x%02x%02x", r, g, b); } - public static @Nullable List showOpenMultipleDialog(FileChooser chooser, Window ownerWindow) { - List files = chooser.showOpenMultipleDialog(ownerWindow); - if (files == null) - return null; - return files.stream().map(File::toPath).toList(); - } - public static FileChooser.ExtensionFilter getImageExtensionFilter() { return new FileChooser.ExtensionFilter(i18n("extension.png"), IMAGE_EXTENSIONS.stream().map(ext -> "*." + ext).toArray(String[]::new)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java index f56b35ed7..50ec4dbae 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java @@ -52,7 +52,7 @@ import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.Platform; -import java.io.*; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -111,9 +111,9 @@ public final class JavaManagementPage extends ListPageBase { + JavaManager.getAddJavaTask(file).whenComplete(Schedulers.javafx(), exception -> { if (exception != null) { LOG.warning("Failed to add java", exception); Controllers.dialog(i18n("java.add.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java index 66c4960a2..b6d31e4f9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java @@ -236,7 +236,7 @@ public abstract class SettingsView extends StackPane { } public void openLogFolder() { - FXUtils.openFolder(LOG.getLogFile().getParent().toFile()); + FXUtils.openFolder(LOG.getLogFile().getParent()); } protected abstract void onUpdate(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java index 74c8a612a..c44a75c4e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTEditorPage.java @@ -33,7 +33,7 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.StringUtils; -import java.io.*; +import java.io.IOException; import java.nio.file.Path; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java index c010a46bc..ceab27504 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/nbt/NBTFileType.java @@ -25,7 +25,11 @@ import com.github.steveice10.opennbt.tag.builtin.Tag; import kala.compress.utils.BoundedInputStream; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 79e771948..9a0fee6b5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -175,7 +175,7 @@ public final class ModListPage extends ListPageBase impl @Override void onReveal() { - FXUtils.openFolder(path.toFile()); + FXUtils.openFolder(path); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index 32a6c4097..bb129624a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -157,7 +157,7 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage } private void onBrowse(String sub) { - FXUtils.openFolder(getProfile().getRepository().getRunDirectory(getVersion()).resolve(sub).toFile()); + FXUtils.openFolder(getProfile().getRepository().getRunDirectory(getVersion()).resolve(sub)); } private void redownloadAssetIndex() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 743f604e3..59cc48f71 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -142,7 +142,7 @@ public final class Versions { } public static void openFolder(Profile profile, String version) { - FXUtils.openFolder(profile.getRepository().getRunDirectory(version).toFile()); + FXUtils.openFolder(profile.getRepository().getRunDirectory(version)); } public static void duplicateVersion(Profile profile, String version) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java index acfa9de8d..5927c4414 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java @@ -87,7 +87,7 @@ public final class WorldListItem extends Control { } public void reveal() { - FXUtils.openFolder(world.getFile().toFile()); + FXUtils.openFolder(world.getFile()); } public void showManagePage() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java index 4109b9e01..f99faa459 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java @@ -117,7 +117,7 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, chunkBaseMenuItem.getWidth(), 0))); } - toolbar.addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OPEN, () -> FXUtils.openFolder(world.getFile().toFile()), null); + toolbar.addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OPEN, () -> FXUtils.openFolder(world.getFile()), null); BorderPane.setMargin(toolbar, new Insets(0, 0, 12, 0)); left.setBottom(toolbar); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java index 3018674d6..eea3f18f2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.ChecksumMismatchException; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.UnsupportedPlatformException; import org.tukaani.xz.LZMAInputStream; @@ -119,7 +120,7 @@ public final class MojangJavaDownloadTask extends Task dest.toFile().setExecutable(true))); + dependencies.add(task.thenRunAsync(() -> FileUtils.setExecutable(dest))); } else { dependencies.add(task); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 52a339d77..0dc412da3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -736,7 +736,8 @@ public class DefaultLauncher extends Launcher { } } } - if (!scriptFile.toFile().setExecutable(true)) + FileUtils.setExecutable(scriptFile); + if (!Files.isExecutable(scriptFile)) throw new PermissionException(); if (usePowerShell && !CommandBuilder.hasExecutionPolicy()) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java index 8e410470b..f47cb0cfe 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java @@ -223,7 +223,8 @@ public class CacheRepository { return cacheData(connection, () -> { String hash = DigestUtils.digestToString(SHA1, bytes); Path cached = getFile(SHA1, hash); - FileUtils.writeBytes(cached, bytes); + Files.createDirectories(cached.getParent()); + Files.write(cached, bytes); return new CacheResult(hash, cached); }); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index a42cc4c28..0a7043f23 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -29,6 +29,8 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; @@ -236,36 +238,6 @@ public final class FileUtils { return new String(bytes, OperatingSystem.NATIVE_CHARSET); } - /** - * Write plain text to file. Characters are encoded into bytes using UTF-8. - *

- * We don't care about platform difference of line separator. Because readText accept all possibilities of line separator. - * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. - * All characters in text will be written into the file in binary format. Existing data will be erased. - * - * @param file the path to the file - * @param text the text being written to file - * @throws IOException if an I/O error occurs - */ - public static void writeText(Path file, String text) throws IOException { - Files.createDirectories(file.getParent()); - Files.writeString(file, text); - } - - /** - * Write byte array to file. - * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. - * All bytes in byte array will be written into the file in binary format. Existing data will be erased. - * - * @param file the path to the file - * @param data the data being written to file - * @throws IOException if an I/O error occurs - */ - public static void writeBytes(Path file, byte[] data) throws IOException { - Files.createDirectories(file.getParent()); - Files.write(file, data); - } - public static void deleteDirectory(Path directory) throws IOException { if (!Files.exists(directory)) return; @@ -285,6 +257,24 @@ public final class FileUtils { } } + public static void setExecutable(Path path) { + PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView.class); + if (view != null) { + try { + Set oldPermissions = view.readAttributes().permissions(); + if (oldPermissions.contains(PosixFilePermission.OWNER_EXECUTE)) + return; + + EnumSet permissions = EnumSet.noneOf(PosixFilePermission.class); + permissions.addAll(oldPermissions); + permissions.add(PosixFilePermission.OWNER_EXECUTE); + view.setPermissions(permissions); + } catch (IOException e) { + LOG.warning("Failed to set permissions for " + path, e); + } + } + } + /** * Copy directory. * Paths of all files relative to source directory will be the same as the ones relative to destination directory. @@ -397,7 +387,8 @@ public final class FileUtils { FileUtils.copyFile(file, targetFile); } - FileUtils.writeText(infoFile, "[Trash Info]\nPath=" + file.toAbsolutePath().normalize() + "\nDeletionDate=" + time + "\n"); + Files.createDirectories(infoDir); + Files.writeString(infoFile, "[Trash Info]\nPath=" + FileUtils.getAbsolutePath(file) + "\nDeletionDate=" + time + "\n"); FileUtils.forceDelete(file); } catch (IOException e) { LOG.warning("Failed to move " + file + " to trash", e);