I'm in process of creating a desktop software which takes regular backup of user's data as per the scheduler he/she has configured. It also has a feature to manually upload files. I'm using ProgressIndicator
in a tablecell
to indicate the upload status.
Here's the problem:
The ProgressIndicator
works fine if I just upload a single file and wait for upload process to finish
but if I try to upload another file before the 1st file gets uploaded completely then the
ProgressIndicator
resets to 0% and it starts uploading both.
This is the code of my work (which I compiled from different examples found on 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 (see onUploadFileAction()
method)
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 (see uploadFile()
method)
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);
}
}
Let me know where I'm doing it wrong
I believe your error lies in onUploadFileAction()
method, as you've stated.
If I run through this the progresListener
, I notice you use the variable upload
in it, and then immediately after this listener
is created, you set the upload
variable. So if you upload two files back to back, whenever a ProgressEvent
is thrown on the first file, it will update the indicator using the upload
variable newly set by the second file, assuming that event is not TRANSFER_COMPLETED_EVENT
then it will display the second files upload progress.
In order to solve this, move upload
into the method and make it an ObjectProperty<Upload>
so you can use it within the lambda.
Hope this helps.
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));