Реализация S3 ProgressListener для ProgressIndicator в TableView JavaFX

Я нахожусь в процессе создания настольного программного обеспечения, которое выполняет регулярное резервное копирование данных пользователя в соответствии с настроенным им планировщиком. Он также имеет функцию для ручной загрузки файлов. я использую ProgressIndicator в tablecell чтобы указать статус загрузки.

Вот проблема:

ProgressIndicator работает нормально, если я просто загружаю один файл и жду окончания процесса загрузки
но если я попытаюсь загрузить другой файл до того, как первый файл будет загружен полностью, то ProgressIndicator сбрасывается до 0% и начинается загрузка обоих.
Это код моей работы (который я собрал из разных примеров, найденных на SO)
Directory.java

import java.util.Objects;
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.control.Button;
import javafx.util.Callback;

public class Directory {

    private String filename;
    private String fileLocation;
    private String datetime;
    private String filePath;
    private String lastUpdated;
    private String userdir_id;
    private String userid;
    private String directory;
    private String device_id;
    private String devicetype;
    private DoubleProperty progressIndicator;
    private String fileType;
    Button addSchedular;

    public Directory() {
    }

    public Directory(String fileName, String fileLocation, String date, String filePath, String fileType, double progressValue) {
        this.filename = fileName;
        this.fileLocation = fileLocation;
        this.datetime = date;
        this.filePath = filePath;
        this.fileType = fileType;
        this.progressIndicator = new SimpleDoubleProperty(progressValue);
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public String getFileLocation() {
        return fileLocation;
    }

    public void setFileLocation(String fileLocation) {
        this.fileLocation = fileLocation;
    }

    public String getDatetime() {
        return datetime;
    }

    public void setDatetime(String datetime) {
        this.datetime = datetime;
    }

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public String getLastUpdated() {
        return lastUpdated;
    }

    public void setLastUpdated(String lastUpdated) {
        this.lastUpdated = lastUpdated;
    }

    public String getUserdir_id() {
        return userdir_id;
    }

    public void setUserdir_id(String userdir_id) {
        this.userdir_id = userdir_id;
    }

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }

    public String getDirectory() {
        return directory;
    }

    public void setDirectory(String directory) {
        this.directory = directory;
    }

    public String getDevice_id() {
        return device_id;
    }

    public void setDevice_id(String device_id) {
        this.device_id = device_id;
    }

    public String getDevicetype() {
        return devicetype;
    }

    public void setDevicetype(String devicetype) {
        this.devicetype = devicetype;
    }

    public double getProgressIndicator() {
        return progressIndicator.get();
    }

    public DoubleProperty getProgressIndicatorProperty() {
        return progressIndicator;
    }

    public void setProgressIndicator(double progressIndicator) {
        this.progressIndicator = new SimpleDoubleProperty(progressIndicator);
    }

    public String getFileType() {
        return fileType;
    }

    public void setFileType(String fileType) {
        this.fileType = fileType;
    }

    public Button getAddSchedular() {
        return addSchedular;
    }

    public void setAddSchedular(Button addSchedular) {
        this.addSchedular = addSchedular;
    }

    public static Callback<Directory, Observable[]> extractor() {
        return (Directory d) -> new Observable[]{d.getProgressIndicatorProperty()};
    }

    @Override
    public boolean equals(Object object) {
        boolean result = false;
        if (object == null || object.getClass() != getClass()) {
            result = false;
        } else {
            Directory bean = (Directory) object;
            if (this.filename.equals(bean.getFilename()) && this.fileLocation.equals(bean.getFileLocation())) {
                result = true;
            }
        }
        return result;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 31 * hash + Objects.hashCode(this.filename);
        hash = 31 * hash + Objects.hashCode(this.fileLocation);
        hash = 31 * hash + Objects.hashCode(this.filePath);
        hash = 31 * hash + Objects.hashCode(this.userid);
        hash = 31 * hash + Objects.hashCode(this.device_id);
        hash = 31 * hash + Objects.hashCode(this.fileType);
        return hash;
    }

    @Override
    public String toString() {
        return "DirectoryBean{" + "filename:" + filename + ", fileLocation:" + fileLocation + ", datetime:" + datetime + ", filePath:" + filePath + ", progressIndicator:" + progressIndicator + ", lastUpdated:" + lastUpdated + ", fileType:" + fileType + ", addSchedular:" + addSchedular + '}';
    }
}  

DirectoriesController.java (см. onUploadFileAction() метод)

import com.amazonaws.AmazonClientException;
import com.amazonaws.event.ProgressEvent;
import com.amazonaws.event.ProgressEventType;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.services.s3.transfer.Upload;
import com.rayz.officebackup.Main;
import com.rayz.officebackup.models.Directory;
import com.rayz.officebackup.services.S3Service;
import com.rayz.officebackup.tablecells.ProgressIndicatorCell;
import com.rayz.officebackup.tablecells.SchedulerButtonCell;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * FXML Controller class
 *
 * 
 */
public class DirectoriesController {

    @FXML
    AnchorPane directoryPane;
    @FXML
    TableView directoryTable;
    @FXML
    TableColumn<Directory, String> fileNameColumn;
    @FXML
    TableColumn<Directory, String> fileLocationColumn;
    @FXML
    TableColumn<Directory, String> dateColumn;
    @FXML
    TableColumn<Directory, Double> progressColumn;
    @FXML
    TableColumn<Directory, String> schedularColumn;
    private static final Logger LOG = LoggerFactory.getLogger(DirectoriesController.class);
    Upload upload = null;
    Map<String, List<String>> dirMap = new HashMap<>();
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    String[] fileType = {"Uploaded", "Scheduled"};

    public DirectoriesController() {
    }

    /**
     * Initializes the controller class.
     *
     */
    @FXML
    private void initialize() {
        fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("filename"));
        fileLocationColumn.setCellValueFactory(new PropertyValueFactory<>("fileLocation"));
        dateColumn.setCellValueFactory(new PropertyValueFactory<>("datetime"));
        progressColumn.setCellValueFactory(new PropertyValueFactory<>("progressIndicator"));
        progressColumn.setCellFactory((TableColumn<Directory, Double> progressIndicatorColumn) -> new ProgressIndicatorCell());
        schedularColumn.setCellValueFactory(new PropertyValueFactory<>("fileType"));
        schedularColumn.setCellFactory((TableColumn<Directory, String> schedulerColumn) -> new SchedulerButtonCell(directoryTable));
        //directoryList is an ObservableList which holds records for directoryTable
        directoryTable.setItems(MainController.directoryList);
    }

    @FXML
    private void onUploadFileAction(MouseEvent event) {
        try {
            S3Service s3Service = new S3Service();
            FileChooser chooser = new FileChooser();
            chooser.setTitle("Select File(s)");
            List<File> files = chooser.showOpenMultipleDialog(Main.mainStage);
            for (File file : files) {
                Optional<Directory> optionalFile = MainController.directoryList.stream().filter(d -> {
                    return d.getFilename().equalsIgnoreCase(file.getName()) && d.getFilePath().equalsIgnoreCase(file.getAbsolutePath());
                }).findFirst();
                if (optionalFile.isPresent()) {
                    warnBox("Warning", "File Exists", "File " + file.getName() + " already exists!");
                } else {
                    Directory directory = new Directory(file.getName(), file.getAbsolutePath(), dateFormat.format(new Date()), file.toPath().toString(), fileType[0], ProgressIndicator.INDETERMINATE_PROGRESS);
                    MainController.directoryList.add(0, directory);
                    directoryTable.setItems(MainController.directoryList);
                    updateDirMap(file.getName(), file.getAbsolutePath());
//<editor-fold>
                    ProgressListener progressListener = (ProgressEvent progressEvent) -> {
                        if (upload == null) {
                            return;
                        }
                        Platform.runLater(() -> {
                            directory.setProgressIndicator(upload.getProgress().getPercentTransferred() / 100.0d);
                            MainController.directoryList.set(MainController.directoryList.indexOf(directory), directory);
                        });
                        if (progressEvent.getEventType() == ProgressEventType.TRANSFER_COMPLETED_EVENT) {
                            Platform.runLater(() -> {
                                directory.setProgressIndicator(1.0d);
                                MainController.directoryList.set(MainController.directoryList.indexOf(directory), directory);
                            });
                        } else if (progressEvent.getEventType() == ProgressEventType.TRANSFER_FAILED_EVENT) {
                            try {
                                AmazonClientException ex = upload.waitForException();
                                Platform.runLater(() -> {
                                    Alert alert = new Alert(Alert.AlertType.ERROR);
                                    alert.setTitle("Error Uploading File");
                                    alert.setContentText("Unable to upload file to Amazon S3:" + ex.getMessage());
                                    alert.showAndWait();
                                });
                            } catch (InterruptedException e) {
                                Thread.currentThread().interrupt();
                            }
                        }
                    };
                    //</editor-fold>
                    upload = s3Service.uploadFile(file, file.length(), file.getName(), progressListener);
                }
            }
        } catch (FileNotFoundException ex) {
            LOG.error(ex.getMessage(), ex);
        }
    }

    @FXML
    private void onUploadDirectoryAction(MouseEvent event) {
        DirectoryChooser directoryChooser = new DirectoryChooser();
        File directory = directoryChooser.showDialog(((Node) event.getSource()).getScene().getWindow());
        if (directory != null) {
            Optional<Directory> optionalDirectory = MainController.directoryList.stream().filter(d -> {
                return d.getFilename().equalsIgnoreCase(directory.getName()) && d.getFilePath().equalsIgnoreCase(directory.getAbsolutePath());
            }).findFirst();
            if (optionalDirectory.isPresent()) {
                warnBox("Warning", "Directory Exists", "Directory " + directory.getName() + " already exists!");
            } else {
                MainController.directoryList.add(0, new Directory(directory.getName(), directory.getAbsolutePath(), null, directory.toPath().toString(), fileType[1], ProgressIndicator.INDETERMINATE_PROGRESS));
                directoryTable.setItems(MainController.directoryList);
                updateDirMap(directory.getName(), directory.getAbsolutePath());
            }
        }
    }

    @FXML
    private void onDeleteAction(MouseEvent event) {
    }

    private void updateDirMap(String dirName, String filePath) {
        if (dirMap.containsKey(dirName)) {
            dirMap.get(dirName).add(filePath);
        } else {
            List<String> list = new ArrayList<>();
            list.add(filePath);
            dirMap.put(dirName, list);
        }
    }

    private void alertBox(String infoMessage, String titleBar, String headerMessage) {
        Platform.runLater(() -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setTitle(titleBar);
            alert.setHeaderText(headerMessage);
            alert.setContentText(infoMessage);
            alert.showAndWait();
        });
    }

    private void warnBox(String infoMessage, String titleBar, String headerMessage) {
        Platform.runLater(() -> {
            Alert alert = new Alert(Alert.AlertType.WARNING);
            alert.setTitle(titleBar);
            alert.setHeaderText(headerMessage);
            alert.setContentText(infoMessage);
            alert.showAndWait();
        });
    }
}  

S3Service.java (см. uploadFile() метод)

import com.amazonaws.AmazonServiceException;
import com.amazonaws.SDKGlobalConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.event.ProgressEvent;
import com.amazonaws.event.ProgressEventType;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.CopyObjectRequest;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsV2Request;
import com.amazonaws.services.s3.model.ListObjectsV2Result;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.services.s3.transfer.MultipleFileDownload;
import com.amazonaws.services.s3.transfer.MultipleFileUpload;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
import com.amazonaws.services.s3.transfer.Upload;
import com.rayz.officebackup.controllers.MainController;
import com.rayz.officebackup.models.Folder;
import com.rayz.officebackup.models.IFile;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.scene.control.Alert;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class S3Service {

    private final String AWS_S3_BUCKET = "s3-bucket";
    private String AWS_S3_BUCKET;
    private static final String AWS_ACCESS_KEY = "access-key";
    private static final String AWS_SECRET_KEY = "secret-key";
    private final static AWSCredentials CREDENTIALS = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
    private final static AmazonS3Client S3CLIENT = new AmazonS3Client(CREDENTIALS);
    private static final String RESOURCE_CONTENT_TYPE = "text/xml";
    private static final Logger LOG = LoggerFactory.getLogger(S3Service.class);

    public MultipleFileUpload uploadDirectory(File directory, ProgressListener listener) throws FileNotFoundException {
        System.setProperty(SDKGlobalConfiguration.ENABLE_IN_REGION_OPTIMIZED_MODE, "true");
        TransferManager tm = new TransferManager(S3CLIENT);
        MultipleFileUpload upload = tm.uploadDirectory(AWS_S3_BUCKET, directory.getName(), directory, true);
        upload.addProgressListener(listener);
        return upload;
    }

    public void downloadDirectory(String key, File downloadDirectory) {
        System.setProperty(SDKGlobalConfiguration.ENABLE_IN_REGION_OPTIMIZED_MODE, "true");
        TransferManager tm = TransferManagerBuilder.standard().withS3Client(S3CLIENT).build();
        MultipleFileDownload download = tm.downloadDirectory(AWS_S3_BUCKET, key, downloadDirectory);
    }

    public Upload uploadFile(File file, long fileSize, String filePath, ProgressListener listener) throws FileNotFoundException {
        System.setProperty(SDKGlobalConfiguration.ENABLE_IN_REGION_OPTIMIZED_MODE, "true");
        ObjectMetadata objectMetaData = new ObjectMetadata();
        objectMetaData.setContentLength(fileSize);
        objectMetaData.setContentType(RESOURCE_CONTENT_TYPE);
        objectMetaData.setCacheControl("public");

        PutObjectRequest putObjectRequest = new PutObjectRequest(AWS_S3_BUCKET, filePath, file)
                .withCannedAcl(CannedAccessControlList.PublicRead)
                .withGeneralProgressListener(listener);
        TransferManager tm = new TransferManager(S3CLIENT);
        return tm.upload(putObjectRequest);
    }
}  

Дайте мне знать, где я делаю это неправильно

1 ответ

Решение

Я считаю, что ваша ошибка заключается в onUploadFileAction() метод, как вы заявили.

Если я пробежусь через это progresListenerЯ заметил, что вы используете переменную upload в нем, а затем сразу после этого listener создан, вы устанавливаете upload переменная. Так что, если вы загружаете два файла вплотную, всякий раз, когда ProgressEvent бросается на первый файл, он обновит индикатор, используя upload переменная, вновь установленная вторым файлом, при условии, что событие не TRANSFER_COMPLETED_EVENT тогда он будет отображать второй процесс загрузки файлов.

Чтобы решить эту проблему, двигайтесь upload в метод и сделать его ObjectProperty<Upload> так что вы можете использовать его в лямбде.

Надеюсь это поможет.

ObjectProperty<Upload> upload = new SimpleObjectProperty();
ProgressListener progressListener = (ProgressEvent progressEvent) -> {
if (upload.get() == null) {
    return;
}
Platform.runLater(() -> {
    directory.setProgressIndicator(upload.get().getProgress().getPercentTransferred() / 100.0d);
    MainController.directoryList.set(MainController.directoryList.indexOf(directory), directory);
});
if (progressEvent.getEventType() == ProgressEventType.TRANSFER_COMPLETED_EVENT) {
    Platform.runLater(() -> {
        directory.setProgressIndicator(1.0d);
        MainController.directoryList.set(MainController.directoryList.indexOf(directory), directory);
    });
} else if (progressEvent.getEventType() == ProgressEventType.TRANSFER_FAILED_EVENT) {
    try {
        AmazonClientException ex = upload.get().waitForException();
        Platform.runLater(() -> {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setTitle("Error Uploading File");
            alert.setContentText("Unable to upload file to Amazon S3:" + ex.getMessage());
            alert.showAndWait();
        });
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}
};
//</editor-fold>
upload.set(s3Service.uploadFile(file, file.length(), file.getName(), progressListener));
Другие вопросы по тегам