修复导出日志文件时文件为空的问题 (#6649)
Some checks failed
Check Codes / build (push) Failing after 3s
Mirror Repository / Mirror to CNB (push) Failing after 0s
Java CI / build (push) Failing after 3s
Mirror Repository / Mirror to Gitee (push) Failing after 1s

This commit is contained in:
Glavo 2026-08-11 21:36:51 +08:00 committed by GitHub
parent a0d1fa8f38
commit cf21cf4a3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 70 additions and 4 deletions

View file

@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.game;
import kala.encdet.EncodingDetector;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.io.Zipper;
import org.jackhuang.hmcl.util.logging.Logger;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@ -90,7 +90,7 @@ public final class LogExporter {
for (Path file : stream) {
if (Files.isRegularFile(file)) {
if (logMatcher == null || logMatcher.matches(file)) {
try (BufferedReader reader = EncodingDetector.MODERN_WEB.newBufferedReader(file)) {
try (BufferedReader reader = IOUtils.newBufferedReaderMaybeNativeEncoding(file)) {
zipper.putLines(reader.lines().map(Logger::filterForbiddenToken), file.getFileName().toString());
} catch (IOException e) {
LOG.warning("Failed to read log file: " + file, e);

View file

@ -33,7 +33,6 @@ import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import kala.encdet.EncodingDetector;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.*;
@ -49,6 +48,7 @@ import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Log4jLevel;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.logging.Logger;
import org.jackhuang.hmcl.util.platform.*;
@ -149,7 +149,7 @@ public class GameCrashWindow extends Stage {
String log;
try {
log = EncodingDetector.MODERN_WEB.readString(latestLog);
log = FileUtils.readTextMaybeNativeEncoding(latestLog);
} catch (IOException e) {
LOG.warning("Failed to read logs/latest.log", e);
return pair(new HashSet<CrashReportAnalyzer.Result>(), new HashSet<String>());

View file

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.util.io;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import kala.encdet.EncodingDetector;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@ -38,6 +39,8 @@ import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
/**
@ -235,6 +238,22 @@ public final class FileUtils {
}
}
public static String readTextMaybeNativeEncoding(Path file) throws IOException {
byte[] bytes = Files.readAllBytes(file);
if (OperatingSystem.NATIVE_CHARSET == UTF_8)
return new String(bytes, UTF_8);
EncodingDetector detector = EncodingDetector.MODERN_WEB;
EncodingDetector.@Nullable Encoding bestEncoding = detector.detect(bytes).bestEncoding();
@Nullable Charset detectedCharset = bestEncoding != null ? bestEncoding.approximateCharset() : null;
if (detectedCharset != null && (detectedCharset == UTF_8 || detectedCharset == US_ASCII))
return new String(bytes, UTF_8);
else
return new String(bytes, OperatingSystem.NATIVE_CHARSET);
}
public static void deleteDirectory(Path directory) throws IOException {
if (!Files.exists(directory))
return;

View file

@ -17,11 +17,20 @@
*/
package org.jackhuang.hmcl.util.io;
import kala.encdet.EncodingDetector;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.GZIPInputStream;
import static java.nio.charset.StandardCharsets.*;
import static org.jackhuang.hmcl.util.platform.OperatingSystem.NATIVE_CHARSET;
/**
* This utility class consists of some util methods operating on InputStream/OutputStream.
@ -35,6 +44,44 @@ public final class IOUtils {
public static final int DEFAULT_BUFFER_SIZE = 32 * 1024;
public static BufferedReader newBufferedReaderMaybeNativeEncoding(Path file) throws IOException {
if (NATIVE_CHARSET == UTF_8)
return Files.newBufferedReader(file);
FileChannel channel = FileChannel.open(file);
try {
long oldPosition = channel.position();
long size = channel.size();
EncodingDetector detector = EncodingDetector.MODERN_WEB;
int bufferSize = (int) Math.max(Math.min(size - oldPosition, detector.maxBytes()), 8192L);
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
//noinspection StatementWithEmptyBody
while (buffer.hasRemaining() && channel.read(buffer) > 0) {
// do nothing
}
buffer.flip();
Charset charset;
if (buffer.remaining() == 0) {
charset = UTF_8;
} else {
EncodingDetector.@Nullable Encoding encoding = detector.detect(buffer).bestEncoding();
Charset detectedCharset = encoding != null ? encoding.approximateCharset() : null;
charset = detectedCharset != null && (detectedCharset == UTF_8 || detectedCharset == US_ASCII)
? UTF_8
: NATIVE_CHARSET;
}
channel.position(oldPosition);
return new BufferedReader(new InputStreamReader(Channels.newInputStream(channel), charset));
} catch (Throwable e) {
closeQuietly(channel, e);
throw e;
}
}
public static byte[] readFully(InputStream stream) throws IOException {
try (stream) {
return stream.readAllBytes();