Downloading file/files in Java. Multithreading, this works? - java

First, everyone needs to know i'm relatively new to Java coding. To be more precise i'm completely new to Object Oriented Programming.
To the question.
I am trying to create a download class that updates a progress bar it was given to show its progress. And possibly anything else I decide to give it in the future to update.
The issue currently is that, in my head, this shouldn't work. I can do anything i want on the "main" method and the GUI is still responsive and quick. In my experience in past programming, this is not possible unless i thread the GUI. Why is this?
Since it works, is this ok to do it this way?
Class Main
package atomicElectronics;
import java.io.IOException;
import atomicElectronics.physical.AtomFrame;
import atomicElectronics.utility.Download;
public class Initial {
static AtomFrame atomLauncher;
public static void main(String[] args) {
atomLauncher = new AtomFrame();
atomLauncher.start();
System.out.println(Integer.MAX_VALUE);
Download theDownload = new Download();
theDownload.fileProgressBar(atomLauncher.progressBar);
try {
theDownload.exicute("http://download.videolan.org/pub/videolan/vlc/last/win64/vlc-2.1.3-win64.exe", "C:\\Users\\TrinaryAtom\\AppData\\Roaming");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO Add Download Methods
// theDownload.updateBarTotal(JProgressBar);
// theDownload.updateLabelSpeed(String);
// theDownload.updateLabelTotal(String);
// theDownload.addFile(File);
// theDownload.addFiles(Files);
}
}
Class AtomFrame
package atomicElectronics.physical;
import javax.swing.JFrame;
import java.awt.FlowLayout;
import javax.swing.JProgressBar;
public class AtomFrame extends JFrame{
public JProgressBar progressBar;
private static final long serialVersionUID = 4010489530693307355L;
public static void main(String[] args){
AtomFrame testFrame = new AtomFrame();
testFrame.start();
}
public AtomFrame(){
initializeComponents();
}
public void initializeComponents(){
this.setSize(400, 400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Atom Launcher");
this.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
progressBar = new JProgressBar();
this.add(progressBar);
//this.pack();
}
public void start() {
this.setVisible(true);
}
public void close() {
this.dispose();
}
}
Class Download
package atomicElectronics.utility;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.swing.JProgressBar;
public class Download {
private static final int BUFFER_SIZE = 4096;
private JProgressBar fileProgressBar;
public Download() {
}
public void fileProgressBar(JProgressBar fileBar) {
fileProgressBar = fileBar;
}
public void exicute(String fileURL, String saveDir) throws IOException {
URL url = new URL(fileURL);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
int responseCode = httpConn.getResponseCode();
// always check HTTP response code first
if (responseCode == HttpURLConnection.HTTP_OK) {
String fileName = "";
String disposition = httpConn.getHeaderField("Content-Disposition");
String contentType = httpConn.getContentType();
double contentLength = httpConn.getContentLength();
if (disposition != null) {
// extracts file name from header field
int index = disposition.indexOf("filename=");
if (index > 0) {
fileName = disposition.substring(index + 9,
disposition.length());
}
} else {
// extracts file name from URL
fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1,
fileURL.length());
}
System.out.println("Content-Type = " + contentType);
System.out.println("Content-Disposition = " + disposition);
System.out.println("Content-Length = " + contentLength);
System.out.println("fileName = " + fileName);
// opens input stream from the HTTP connection
InputStream inputStream = httpConn.getInputStream();
String saveFilePath = saveDir + File.separator + fileName;
// opens an output stream to save into file
FileOutputStream outputStream = new FileOutputStream(saveFilePath);
double totalRead = 0;
int bytesRead = -1;
byte[] buffer = new byte[BUFFER_SIZE];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalRead += bytesRead;
System.out.println((totalRead / contentLength) * 100);
fileProgressBar.setValue((int)((totalRead / contentLength) * 100));
}
outputStream.close();
inputStream.close();
System.out.println("File downloaded");
} else {
System.out.println("No file to download. Server replied HTTP code: " + responseCode);
}
httpConn.disconnect();
}
}

Suggestions:
Use a SwingWorker to do your background thread work.
Inside your SwingWorker, set its progress "bound" property via setProgress(int progress). The value should be between 1 and 100.
Don't have your SwingWorker/file downloader hold the JProgressBar or any Swing components.
Add a PropertyChangeListener to your SwingWorker and monitor changes in the progress property.
Never make your Swing fields (or most and and all fields) public. Limit access, and instead change object state via methods.
Read the tutorial Concurrency in Swing for the necessary details.
For example, the code below is a gross simplification and downloads no files, but should give you the idea:
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
public class Initial {
static AtomFrame atomLauncher;
public static void main(String[] args) {
atomLauncher = new AtomFrame();
atomLauncher.start();
System.out.println(Integer.MAX_VALUE);
final Download theDownload = new Download();
theDownload.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("progress".equals(pcEvt.getPropertyName())) {
int progress = theDownload.getProgress();
atomLauncher.setProgress(progress);
}
}
});
theDownload.execute();
}
}
class AtomFrame extends JFrame {
// ********* should be private!
private JProgressBar progressBar;
private static final long serialVersionUID = 4010489530693307355L;
public static void main(String[] args) {
AtomFrame testFrame = new AtomFrame();
testFrame.start();
}
public void setProgress(int progress) {
progressBar.setValue(progress);
}
public AtomFrame() {
initializeComponents();
}
public void initializeComponents() {
this.setSize(400, 400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Atom Launcher");
this.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
progressBar = new JProgressBar();
this.add(progressBar);
// this.pack();
}
public void start() {
this.setVisible(true);
}
public void close() {
this.dispose();
}
}
class Download extends SwingWorker<Void, Void> {
private static final long SLEEP_TIME = 300;
private Random random = new Random();
#Override
protected Void doInBackground() throws Exception {
int myProgress = 0;
while (myProgress < 100) {
myProgress += random.nextInt(10);
setProgress(myProgress);
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {}
}
return null;
}
}

Related

ScheduledThreadPoolExecutor scheduled purging memory leak

I am trying to write a enclosed class to download images from the web that will refresh every 30 seconds. I might want to download 1 image or I might want to download N images. And I might want to stop downloading a certain image at any time. I wrote the following class that works great except when I stop downloading an image memory is not being released for that task. Or if I stop all images from being downloaded memory is not released (This won't happen in production). I have tried several different ways to achieve this. My last attempt was to purge the tasks from the ScheduledThreadPoolExecutor every 30 seconds using the same executor or with the code below a separate executor. I am also providing code to test releasing memory, although my example stops all images from being downloaded when in a real life usage I should be able only stop one image and the memory be released from that one task.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
public class ImageLoadTask implements Runnable {
private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500);
private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500);
private static Runnable purgeRunnable = () -> purge();
private ScheduledFuture<?> scheduledFuture;
private URL pictureURL;
private Consumer<BufferedImage> successMethod;
private Consumer<String> failMethod;
private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler();
private boolean displaySuccess = false;
private boolean displayError = false;
private boolean isCancelled = false;
static {
taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
taskExecutorService.setRemoveOnCancelPolicy(true);
purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS);
}
public ImageLoadTask(String url) {
try {
this.pictureURL = new URL(url);
} catch (MalformedURLException e) {
if(failMethod != null) {
failMethod.accept(e.getMessage()); ;
}
if(displayError) {
System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage());
}
}
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) {
this(url);
this.successMethod = successMethod;
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) {
this(url, successMethod);
this.failMethod = failMethod;
}
public void start() {
scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS);
}
public void stop() {
if(isCancelled)
return;
isCancelled = true;
scheduledFuture.cancel(true);
scheduledFuture = null;
pictureURL = null;
successMethod = null;
failMethod = null;
streamHandler = null;
taskExecutorService.remove(this);
taskExecutorService.purge();
}
public static void purge() {
System.out.println("Purging");
taskExecutorService.purge();
}
#Override
public void run() {
if(!isCancelled) {
try {
BufferedImage image = loadImage(pictureURL);
if(displaySuccess) {
System.out.println("Image received for url " + pictureURL);
}
if(successMethod != null && !isCancelled) {
successMethod.accept(image); ;
}
} catch (IOException e) {
if(failMethod != null && !isCancelled) {
failMethod.accept(e.getMessage());
}
if(displayError) {
System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage());
}
}
}
}
public void displayError(boolean displayError) {
this.displayError = displayError;
}
public void displaySuccess(boolean displaySuccess) {
this.displaySuccess = displaySuccess;
}
private BufferedImage loadImage(URL input) throws IOException {
if (input == null) {
throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream = streamHandler.openConnection(input).getInputStream();
} catch (IOException e) {
throw new IIOException("Can't get input stream from URL!", e);
}
ImageInputStream stream = ImageIO.createImageInputStream(istream);
BufferedImage bi;
try {
bi = ImageIO.read(stream);
if (bi == null) {
stream.close();
}
} finally {
istream.close();
}
return bi;
}
#Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
class ImageURLStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL url) throws IOException {
URL target = new URL(url.toString());
URLConnection connection = target.openConnection();
// Connection settings
connection.setConnectTimeout(60000); // 1 min
connection.setReadTimeout(60000); // 1 min
return connection;
}
}
}
Test App:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLoadTaskTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Gui gui = new Gui();
}
});
}
static class Gui extends JFrame {
private static final long serialVersionUID = 1L;
private List<ImageLoadTask> tasks = new ArrayList<>();
private boolean running = false;
private JButton startStopButton = new JButton("Start");
private JButton purgeButton = new JButton("Purge");
private ActionListener startStopListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(running) {
stopTasks();
} else {
startTasks();
}
}
};
private ActionListener purgeListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ImageLoadTask.purge();
}
};
public Gui() {
setTitle("Image Load Task Test");
setBounds(250, 250, 300, 150); // Size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPanel = new JPanel();
setContentPane(contentPanel);
startStopButton.addActionListener(startStopListener);
contentPanel.add(startStopButton);
purgeButton.addActionListener(purgeListener);
contentPanel.add(purgeButton);
setVisible(true);
}
private void startTasks() {
running = true;
System.out.println("Starting tasks");
for(int i = 0; i < 2500; i++) {
ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail);
task.start();
tasks.add(task);
}
startStopButton.setText("Stop");
}
private void stopTasks() {
running = false;
System.out.println("Stopping " + tasks.size() + " tasks");
for(ImageLoadTask task : tasks) {
task.stop();
}
tasks.clear();
startStopButton.setText("Start");
System.out.println("Stopped tasks ");
//ImageLoadTask.purge();
}
private void success(BufferedImage image) {
//System.out.println("Success!");
}
private void fail(String message) {
//System.out.println("Fail! "+ message);
}
}
}
You don't close your 'stream' when you interrupt ImageIO.read(stream).

Why isn't my image downloading when I use a SwingWorker but downloads when I don't?

I'm creating a progress bar to monitor an image download. The image download doesn't work — it generates a file measuring 0 bytes in size. If I move my code to a standalone class without a SwingWorker, the image download works. I've played with this for a while and I still don't know what I'm doing wrong. The two code blocks are identical. Any tips would be much appreciated!
Image download with SwingWorker (take note of doInBackground):
package download_progress_bar;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.net.*;
import java.io.*;
public class ProgressBar implements ActionListener, PropertyChangeListener {
private JFrame frame;
private JPanel gui;
private JButton button;
private JProgressBar progressBar;
private SwingWorker<Void, Void> worker;
private boolean done;
public ProgressBar() {
done = false;
customizeFrame();
createMainPanel();
createProgressBar();
createButton();
addComponentsToFrame();
}
private void customizeFrame() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createMainPanel() {
gui = new JPanel();
gui.setLayout(new BorderLayout());
}
private void createProgressBar() {
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true); // renders a progress string
}
private void createButton() {
button = new JButton("Start download");
button.addActionListener(this);
}
/**
* Invoked when user clicks the button.
*/
public void actionPerformed(ActionEvent evt) {
button.setEnabled(false);
// NOTE: Instances of javax.swing.SwingWorker are not reusable,
// so we create new instances as needed
worker = new Worker();
worker.addPropertyChangeListener(this);
worker.execute();
}
class Worker extends SwingWorker<Void, Void> {
/*
* Main task. Executed in worker thread.
*/
#Override
protected Void doInBackground() throws MalformedURLException {
// Create a URL object for a given URL
String src = "https://lh3.googleusercontent.com/l6JAkhvfxbP61_FWN92j4ulDMXJNH3HT1DR6xrE7MtwW-2AxpZl_WLnBzTpWhCuYkbHihgBQ=s640-h400-e365";
URL url = new URL(src);
// Open connection on the URL object
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Always check response code first
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
System.out.println(responseCode);
// Open input stream from connection
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
// Open output stream for file writing
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("cat.jpg"));
int bytesRead = - 1;
int totalBytesRead = 0;
int percentCompleted = 0;
while ((bytesRead = in.read()) != -1) {
out.write(bytesRead);
totalBytesRead += bytesRead;
percentCompleted = totalBytesRead * 100 / connection.getContentLength();
System.out.println("..." + percentCompleted);
this.setProgress(percentCompleted);
}
// Close streams
out.close();
in.close();
}
} catch (IOException ex) {
System.out.println(ex);
this.setProgress(0);
cancel(true);
}
return null;
}
/*
* Executed in event dispatching thread
*/
#Override
protected void done() {
button.setEnabled(true);
if (!isCancelled()) {
System.out.println("File has been downloaded successfully!");
} else {
System.out.println("There was an error in downloading the file.");
}
}
}
/**
* Invoked when task's progress property changes.
*/
public void propertyChange(PropertyChangeEvent evt) {
System.out.println(evt);
// NOTE: By default two property states exist: "state" and "progress"
if (evt.getPropertyName().equals("progress")) {
int progress = (Integer) evt.getNewValue();
progressBar.setValue(progress);
System.out.println(String.format(
"Completed %d%% of task.\n", progress));
}
}
private void addComponentsToFrame() {
gui.add(progressBar, BorderLayout.CENTER);
gui.add(button, BorderLayout.SOUTH);
frame.add(gui);
frame.pack();
}
public void activate() {
frame.setVisible(true);
}
}
Image download with standalone Downloader class (take note of download):
package download_progress_bar;
import java.net.*;
import java.io.*;
public class Downloader {
public static void main(String[] args) throws IOException {
download();
}
public static void download() throws IOException {
// Create a URL object for a given URL
String src = "https://lh3.googleusercontent.com/l6JAkhvfxbP61_FWN92j4ulDMXJNH3HT1DR6xrE7MtwW-2AxpZl_WLnBzTpWhCuYkbHihgBQ=s640-h400-e365";
URL url = new URL(src);
// Open connection on the URL object
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Always check response code first
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
System.out.println(responseCode);
// Open input stream from connection
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
// Open output stream for file writing
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("test.jpg"));
int bytesRead = - 1;
int totalBytesRead = 0;
int percentCompleted = 0;
while ((bytesRead = in.read()) != -1) {
out.write(bytesRead);
totalBytesRead += bytesRead;
percentCompleted = totalBytesRead * 100 / connection.getContentLength();
System.out.println("..." + percentCompleted);
}
// Close streams
out.close();
in.close();
}
} catch (IOException ex) {
System.out.println(ex);
}
}
}
You should call get() in done(). If doInBackground throws an exception, then get() will throw an ExecutionException whose cause is the exception from doInBackground.
Something like this:
#Override
protected void done() {
button.setEnabled(true);
try {
if (!isCancelled()) {
get();
System.out.println("File has been downloaded successfully!");
return;
}
} catch (InterruptedException x) {
x.printStackTrace();
} catch (ExecutionException x) {
// This should print an IllegalArgumentException
// if me theory (explained below) is correct.
x.getCause().printStackTrace();
}
System.out.println("There was an error in downloading the file.");
}
My theory is that the issue is related to this line:
totalBytesRead += bytesRead;
Since bytesRead is the return value of InputStream.read(), it's actually a byte of data, not the number of bytes read. This doesn't have an obvious effect on the I/O, but it corrupts the value of percentCompleted. This ends up passing a value which is greater than 100 to setProgress which throws an exception. The line should be totalBytesRead++; instead.
You can verify my theory with the aforementioned modification to done().

SwingWorker Exception

I have a some class for downloading a big files from a web sites and displaying a process with jProgressBar.
I am using a SwingWorker class and the method doInBackground. It works fine, but when size of downloaded file > 20mb, i am getting a java.lang.IllegalArgumentException: the value should be from 0 to 100. What am i doing wrong? Please, help me! My piece of code:
#Override
protected Void doInBackground() throws Exception {
URL url = new URL(site);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int filesize = connection.getContentLength();
int totalDataRead = 0;
try (java.io.BufferedInputStream in = new java.io.BufferedInputStream(
connection.getInputStream())) {
java.io.FileOutputStream fos = new java.io.FileOutputStream(file);
try (java.io.BufferedOutputStream bout = new BufferedOutputStream(
fos, 1024)) {
byte[] data = new byte[1024];
int i;
while ((i = in.read(data, 0, 1024)) >= 0) {
totalDataRead = totalDataRead + i;
bout.write(data, 0, i);
int percent = (totalDataRead * 100) / filesize;
setProgress(percent);
}
}
}
return null;
}
}
Having played around with your calculations, this...
int percent = (totalDataRead * 100) / filesize;
Just looks wrong to me (it might just be my pathetic math brain), but out of 40mb at around 20.48mb (or 51%), it reverts to a negative number (-51%)
How ever, if I use
int percent = Math.round(((float)count / (float)range) * 100f);
It works just fine.
Also, I'd consider making filesize and totalDataRead long instead of int, but that's just me.
And a runnable example which I used to test it with...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar pb;
public TestPane() {
pb = new JProgressBar();
setLayout(new GridBagLayout());
add(pb);
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
int range = 40 * (1024 * 1024);
for (int count = 0; count < range; count += 1024) {
int percent = Math.round(((float)count / (float)range) * 100f);
setProgress(percent);
Thread.sleep(1);
}
return null;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (null != evt.getPropertyName()) {
switch (evt.getPropertyName()) {
case "progress":
pb.setValue(worker.getProgress());
break;
}
}
}
});
worker.execute();
}
}
}

Can I combine this into one loop? [duplicate]

This question already has answers here:
How to chain multiple different InputStreams into one InputStream
(5 answers)
Closed 8 years ago.
Hi i have a run method thats inside a Thread that writes either using a fileOutputStream stream or a RandomaccessFile. I have it in 2 while loops I'de like to combine it into 1 but I'm lost as to how. here is the run method.
package treadmakestop;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
/**
*
* #author brett
*/
public class Worker implements Runnable {
private final ReentrantLock PAUSELOCK;
private final Condition PAUSECONDITION;
private final AtomicBoolean PAUSED;
private final AtomicBoolean KEEPRUNNING;
private final AtomicBoolean KILLSTREAM;
private final File FILEIN = new File("C:\\Users\\brett\\Documents\\Back to the Beginning Origins Nova Neil Degrasse Tyson Documentary-D-EXw5CdPtM.mp4");
private final File WORKINGDIR = new File(System.getProperty("user.home") + File.separator + "Documents" + File.separator + "ConvertedTvFiles" + File.separator);
private final File FILEOUT = new File(WORKINGDIR + File.separator + "spew" + ".mp4");
private final JProgressBar TVPG;
private final long LENGTH = FILEIN.length();
private final byte[] b = new byte[1024];
private FileOutputStream FOUTS;
private FileInputStream FINS;
private RandomAccessFile RandFileOut;
private RandomAccessFile RandFileIn;
private int r;
private long Counter = 1;
public Worker(JProgressBar tvpg) {
PAUSED = new AtomicBoolean();
KEEPRUNNING = new AtomicBoolean(true);
KILLSTREAM = new AtomicBoolean(false);
PAUSELOCK = new ReentrantLock();
PAUSECONDITION = PAUSELOCK.newCondition();
TVPG = tvpg;
}
/**
* This is the main thread and copy file operation
*/
#Override
public void run() {
System.out.println("Runnable has started");
int progress;
try {
createFirstFileStream();
while ((r = FINS.read(b)) != -1 && KEEPRUNNING.get()) {
checkPauseState();
if (!KILLSTREAM.get()) {
Counter += r;
FOUTS.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killFileStream();
break;
}
}
createRandomFile();
while ((r = RandFileIn.read(b)) != -1 && KEEPRUNNING.get()) {
checkPauseState();
if (KILLSTREAM.get()) {
Counter += r;
RandFileOut.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killRandFile();
break;
}
}
} catch (IOException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Runnable has exited");
}
public void createFirstFileStream() throws IOException {
FINS = new FileInputStream(FILEIN);
FOUTS = new FileOutputStream(FILEOUT);
}
public void createRandomFile() throws IOException {
RandFileIn = new RandomAccessFile(FILEIN, "rw");
RandFileIn.seek(FILEOUT.length());
long pointer = RandFileIn.getFilePointer();
RandFileOut = new RandomAccessFile(FILEOUT, "rw");
RandFileOut.seek(pointer);
}
public boolean isPaused() {
return PAUSED.get();
}
public void pause() {
KILLSTREAM.set(true);
PAUSED.set(true);
}
public void resume() {
PAUSED.set(false);
PAUSELOCK.lock();
try {
PAUSECONDITION.signal();
} finally {
PAUSELOCK.unlock();
}
}
protected void checkPauseState() {
while (PAUSED.get()) {
PAUSELOCK.lock();
try {
PAUSECONDITION.await();
} catch (Exception e) {
} finally {
PAUSELOCK.unlock();
}
}
}
protected void updateProgress(int progress) {
if (EventQueue.isDispatchThread()) {
TVPG.setValue(progress);
if (KEEPRUNNING.get() == false) {
TVPG.setValue(0);
}
} else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
updateProgress(progress);
}
});
}
}
public void killFileStream() throws IOException {
FINS.close();
FOUTS.flush();
FOUTS.close();
}
public void killRandFile() throws IOException {
RandFileIn.close();
RandFileOut.close();
}
public synchronized void stop() {
KEEPRUNNING.set(false);
resume();
}
}
Here is the whole class
Something like this should work:
createFirstFileStream();
createRandomFile();
InputStream combined = new SequentialInputStream(FINS,
Channels.createInputStream(RandomFileIn.getChannel());
// your while loop
while ((r = combined.read(b)) != -1 && KEEPRUNNING.get()) {
if (!KILLSTREAM.get()) {
Counter += r;
FOUTS.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killFileStream();
break;
}
}
Alternatively, wrap the loop in a function and call it twice, using the Channels call above to wrap the RandomAccessFile to an InputStream.

Progressbar of a bin file loading

how can I show a JProgressBar component like on the loading of a bin file?
I can only found solutions for iterative bin read and I'm using an object reading like:
CustomObj test = (CustomObj) in.readObject();
Cheers
If you can't measure the progress of the process, then you can only specify the "indeterminate mode" of the progress bar. When in this mode, the progress bar will indicate that it is working, but the completion of the process is unknown.
JProgressBar progress = new JProgressBar();
progress.setIndeterminate(true);
I recommend to do two things:
Creating a wrapping class around your original inputstream so that you can monitor the bytes that are read from it. Basically, you extends InputStream and delegate everything to the original stream (except a few methods) and in the read() method, you make sure that you notify some listener.
I guess that if you want a progress bar, it means that the loading operation takes a while and you want to provide feedback to the user. Long running task cannot run directly on the EDT (so typically, you cannot perform your task in an actionPerformed method). You therefore need to delegate the work to another Thread, by using a SwingWorker for example. If you don't this, then the UI will freeze and the feedback will not be viewable by the user.
This being, said it may seem complex or not trivial. Therefore, here some short example, that illustrates all this and works:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class TestProgressBar {
// Some simple listener interface to get a callback as bytes are being read
public static interface ProgressListener {
public void notifyByteRead();
}
// The wrapping input stream that will call the listener as bytes are being read
public static class ProgressInputStream extends InputStream {
private InputStream in;
#Override
public int read() throws IOException {
int read = in.read();
if (read > -1) {
// Here we notify the listener
listener.notifyByteRead();
}
return read;
}
#Override
public long skip(long n) throws IOException {
return in.skip(n);
}
#Override
public int available() throws IOException {
return in.available();
}
#Override
public void close() throws IOException {
in.close();
}
#Override
public void mark(int readlimit) {
in.mark(readlimit);
}
#Override
public void reset() throws IOException {
in.reset();
}
#Override
public boolean markSupported() {
return in.markSupported();
}
private ProgressListener listener;
public ProgressInputStream(InputStream in, ProgressListener listener) {
this.in = in;
this.listener = listener;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
init();
}
});
}
public static void init() {
// 1. Let's create a big object with lots of data
List<Long> object = new ArrayList<Long>();
Random random = new Random();
for (int i = 0; i < 1e6; i++) {
object.add(random.nextLong());
}
// 2. We write it to a temp file
File tempFile = null;
ObjectOutputStream oos = null;
try {
tempFile = File.createTempFile("Test", ".bin");
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
oos = new ObjectOutputStream(new BufferedOutputStream(fos));
oos.writeObject(object);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (tempFile == null) {
System.exit(1);
}
// 3. Now let's build a UI to load that
final File theFile = tempFile;
JFrame frame = new JFrame("Test ghost text");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
final JProgressBar bar = new JProgressBar(0, (int) tempFile.length());
JButton button = new JButton("load");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bar.setValue(0);
// Declare and implement a Swing worker that will run in another thread
SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
#Override
protected void process(List<Integer> chunks) {
// Here we are on the EDT, so we can safely notify the progressbar
super.process(chunks);
bar.setValue(bar.getValue() + chunks.size());
}
#Override
protected Void doInBackground() throws Exception {
// Here we are not in the EDT, we perform the task but don't modify anything in the UI
ProgressInputStream pis = new ProgressInputStream(new BufferedInputStream(new FileInputStream(theFile)),
new ProgressListener() {
#Override
public void notifyByteRead() {
publish(1); // the value that is sent here could be anything, we don't use it.
}
});
ObjectInputStream ois = new ObjectInputStream(pis);
try {
List<Long> readObject = (List<Long>) ois.readObject();
System.err.println("Loaded " + readObject.size() + " long values");
} catch (Exception e) {
e.printStackTrace();
} finally {
pis.close();
}
return null;
}
};
// Start the worker
worker.execute();
}
});
panel.add(bar);
panel.add(button, BorderLayout.EAST);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
Subclass java.io.FilteredInputStream to count the number of bytes being read and insert it between your ObjectInputStream and the underlying InputStream being read.
You can update the progress bar by sampling the running count or using a callback built in to your subclass.
Example:
public class CountingInputStream extends FilteredInputStream {
private int numBytes;
public CountingInputStream(InputStream inputStream){
this(inputStream);
}
public int getNumBytes(){
return numBytes;
}
#Override
public int read() {
int b = super.read();
if(b != -1){
countBytes(1);
}
return b;
}
#Override
public int read(byte[] b){
int n = super.read(b);
if(n >= 0){
countBytes(n);
}
return n;
}
#Override
public int read(byte[] b, int off, int len){
int n = super.read(b, off, len);
if(n >= 0){
countBytes(n);
}
return n;
}
private void countBytes(int n){
numBytes += n;
}
}
It could be used like below (assume InputStream is your source of data).:
InputStream is = ...;
CountingInputStream cis = new CountingInputStream(is)
ObjectInputStream ois = new ObjectInputStream(cis);
ois.readObject();
You can sample cis.getNumBytes() from a different thread (potentially with a Swing timer) and use the returned value to update a JProgressBar

Categories