在 TableView JavaFX 中为 ProgressIndicator 实现 S3 ProgressListener

Implementing S3 ProgressListener for ProgressIndicator in TableView JavaFX

我正在创建一个桌面软件,它会根据调度程序 he/she 的配置定期备份用户数据。它还具有手动上传文件的功能。我在 tablecell 中使用 ProgressIndicator 来指示上传状态。

这是问题所在:

如果我只上传一个文件并等待上传过程完成,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);
    }
}  

让我知道哪里做错了

正如您所说,我认为您的错误在于 onUploadFileAction() 方法。

如果我 运行 通过 progresListener,我注意到您在其中使用了变量 upload,然后在创建此 listener 之后立即设置upload 变量。因此,如果您背靠背上传两个文件,每当第一个文件抛出 ProgressEvent 时,它将使用第二个文件新设置的 upload 变量更新指标,假设事件不是 TRANSFER_COMPLETED_EVENT 然后会显示第二个文件的上传进度。

为了解决这个问题,将 upload 移动到方法中并使其成为 ObjectProperty<Upload> 以便您可以在 lambda 中使用它。

希望这对您有所帮助。

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));