Downloading file/files in Java. Multithreading, th

2020-04-19 04:35发布

问题:

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

}

}

回答1:

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