Простым подходом является использование базового потока ввода, который отслеживает, сколько байтов вы прочитали. Метод Files.size()
даст вам общее количество байтов в файле, так что это даст вам достаточно информации для вычисления общего прогресса. Вы можете сделать что-то вроде
public class CountingInputStream extends InputStream implements AutoCloseable {
private long bytesRead = 0 ;
private final InputStream stream ;
public CountingInputStream(InputStream stream) {
this.stream = stream ;
}
@Override
public int read() throws IOException {
int result = stream.read() ;
if (result != -1) {
bytesRead++;
}
return result ;
}
@Override
public void close() throws IOException {
super.close();
stream.close();
}
public long getBytesRead() {
return bytesRead ;
}
}
Обратите внимание, что если вы заключаете это в BufferedReader
, getBytesRead()
возвращает количество байтов, считанных из основного потока, в том числе тех, которые еще сохраняются в буфере. Вероятно, это достаточно хорошо для отображения индикатора выполнения (так как он очень быстро читается из буфера), но не будет технически на 100% точным.
Здесь находится SSCCE. В моей системе вам нужно загрузить файл с ~ 100 000 строк, чтобы увидеть индикатор выполнения. Вы можете создать его, если у вас нет одного доступного (SSCCE позволяет вам сначала создать файл).
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class ReadFileWithProgress extends Application {
public static class CountingInputStream extends InputStream implements AutoCloseable {
private long bytesRead = 0 ;
private final InputStream stream ;
public CountingInputStream(InputStream stream) {
this.stream = stream ;
}
@Override
public int read() throws IOException {
int result = stream.read() ;
if (result != -1) {
bytesRead++;
}
return result ;
}
@Override
public void close() throws IOException {
stream.close();
}
public long getBytesRead() {
return bytesRead ;
}
}
@Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
TextField numLinesField = new TextField();
FileChooser chooser = new FileChooser();
Button create = new Button("Create File...");
create.setOnAction(e -> {
int numLines = Integer.parseInt(numLinesField.getText());
File file = chooser.showSaveDialog(primaryStage);
if (file != null) {
try {
createFile(file.toPath(), numLines);
} catch (Exception exc) {
exc.printStackTrace();
}
}
});
Button loadFile = new Button("Load file");
ProgressBar progress = new ProgressBar(0);
loadFile.setOnAction(e -> {
File file = chooser.showOpenDialog(primaryStage);
if (file != null) {
Task<Map<String, String>> task = readFileTask(file.toPath());
progress.progressProperty().bind(task.progressProperty());
task.setOnSucceeded(evt -> new Alert(AlertType.INFORMATION, "File loaded", ButtonType.OK).showAndWait());
task.setOnFailed(evt -> new Alert(AlertType.ERROR, "File could not be loaded", ButtonType.OK).showAndWait());
new Thread(task).start();
}
});
root.addRow(0, new Label("Number of lines:"), numLinesField, create);
root.add(loadFile, 0, 1, 3, 1);
root.add(progress, 0, 2, 3, 1);
GridPane.setFillWidth(progress, true);
GridPane.setHalignment(progress, HPos.CENTER);
GridPane.setFillWidth(loadFile, true);
GridPane.setHalignment(loadFile, HPos.CENTER);
root.setPadding(new Insets(20));
root.setHgap(5);
root.setVgap(10);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private Task<Map<String, String>> readFileTask(Path path) {
return new Task<Map<String, String>>() {
@Override
protected Map<String, String> call() throws IOException {
try (
CountingInputStream input = new CountingInputStream(Files.newInputStream(path));
BufferedReader in = new BufferedReader(new InputStreamReader(input));
) {
long totalBytes = Files.size(path);
return in.lines()
.peek(line -> updateProgress(input.getBytesRead(), totalBytes))
.map(line -> line.split("\t"))
.collect(Collectors.toMap(tokens -> tokens[0], tokens -> tokens[1]));
}
}
};
}
private void createFile(Path path, int numEntries) throws IOException {
try (BufferedWriter out = Files.newBufferedWriter(path)) {
for (int i = 1; i <= numEntries ; i++) {
out.write(String.format("key %d\tvalue %d%n", i, i));
}
}
}
public static void main(String[] args) {
launch(args);
}
}
JavaDoc для справки: [Task] (https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html) –
Я думаю, что 'байт read'/'total bytes' будет реальным прогрессом. –
Ваш полезный метод очень неэффективен (это мягко сказано). Чтобы прочитать каждую строку, вы открываете файл с нуля и просматриваете файл до последней строки, а затем получаете следующую строку. Таким образом, чтобы читать 9000 строк, в общей сложности вы читаете 1 + 2 + 3 + ... + 9000 = 40,504,500 строк, открывая и закрывая файл 9000 раз. Возможно, если вы просто прочитаете файл, вам даже не понадобится индикатор прогресса (или он может просто использовать неопределенный вариант). –