#java #javafx #download #progress-bar
#java #javafx #Скачать #индикатор выполнения
Вопрос:
Я создаю приложение JavaFX, которое будет отправлять несколько файлов по сети, и я хочу иметь индикатор выполнения для количества отправленных байтов. У меня уже есть общий размер файлов, которые собираются быть отправлены, однако я не знаю, как увеличить индикатор выполнения.
Проблема в том, что я отправляю файлы одновременно, поэтому я не могу просто установить прогресс в своем классе загрузки, так как это будет перезаписано другой угрозой.
Вот как выглядит мой класс загрузки:
iStream = socket.getInputStream();
bufferSize = socket.getReceiveBufferSize();
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
byte[] bytes = new byte[bufferSize];
int count;
while ((count = is.read(bytes)) >= 0) {
bos.write(bytes, 0, count);
}
bos.close();
is.close();
И я выполняю класс следующим образом:
List<Callable<String>> downloadTasks = new ArrayList<>();
Callable<String> download = new Download(new File(path), portnumber);
downloadTasks.add(download);
es.invokeAll(downloadTasks);
У меня есть общее количество байтов в переменной long totalSize
в обоих классах.
Можете ли вы, пожалуйста, помочь мне в том, как я мог бы достичь индикатора выполнения байтов? Спасибо
Ответ №1:
Вы можете обновлять индикатор выполнения только из одного потока: потока приложения FX. Таким образом, проблема с потоками является спорной; вы можете просто увеличить свойство, представляющее общее количество загруженных байтов, и организовать его обновление только из этого потока.
Итак, что-то вроде:
public class UploadProgressMonitor {
private final long totalBytes ;
private long downloadedBytes ;
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper() ;
public UploadProgressMonitor(long totalBytes) {
this.totalBytes = totalBytes ;
}
private void doIncrement(long bytes) {
downloadedBytes = bytes ;
progress.set(1.0 * downloadedBytes / totalBytes);
}
public void increment(long bytes) {
if (Platform.isFxApplicationThread()) {
doIncrement(bytes);
} else {
Platform.runLater(() -> doIncrement(bytes));
}
}
public ReadOnlyDoubleProperty progressProperty() {
return progress.getReadOnlyProperty() ;
}
public final double getProgress() {
return progressProperty().get();
}
}
Теперь вы можете делать такие вещи, как:
long totalBytes = ... ;
UploadProgressMonitor monitor = new UploadProgressMonitor(totalBytes);
progressBar.progressProperty().bind(monitor.progressProperty());
и если вы дадите каждой задаче ссылку на UploadProgressMonitor
то, что ей просто нужно сделать:
int count;
while ((count = is.read(bytes)) >= 0) {
bos.write(bytes, 0, count);
monitor.increment(count);
}
Комментарии:
1. Спасибо, это работает, но проблема в том, что индикатор выполнения останавливается примерно на 90%. Если я вычислю размер int count после загрузки, это составит около 90% от общего размера в байтах, сообщаемого OS / Files.size(). Есть ли у вас какие-либо советы о том, как это исправить?
2. Я думаю, что это как-то связано с потоками. Если я отправлю только 1 файл, он будет равен 100%. Если я отправлю два одинаковых файла, это составит около 50%.
3. @Peter Нет … для отладки вы можете проверить
count
, что для каждой загрузки равен ожидаемому общему размеру для отдельного файла. Убедитесь, что вы правильно вычисляете итоговые значения, и убедитесь, что каждый раз, когда вы отправляете буфер, вы увеличиваете монитор (например, убедитесь, что вы не пропустили последнюю или какую-либо подобную ошибку). Это просто звучит так, как будто в вашем коде есть ошибка. Опять же, опубликованный мной код в основном однопоточный.4. Извините, я это исправил. Это работает просто отлично. Я допустил довольно серьезную ошибку и создал новый монитор прогресса загрузки для каждого потока….