13

How can I use the library to download a file and print out bytes saved? I tried using

import static org.apache.commons.io.FileUtils.copyURLToFile;
public static void Download() {

        URL dl = null;
        File fl = null;
        try {
            fl = new File(System.getProperty("user.home").replace("\\", "/") + "/Desktop/Screenshots.zip");
            dl = new URL("http://ds-forums.com/kyle-tests/uploads/Screenshots.zip");
            copyURLToFile(dl, fl);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

but I cannot display bytes or a progress bar. Which method should I use?

public class download {
    public static void Download() {
        URL dl = null;
        File fl = null;
        String x = null;
        try {
            fl = new File(System.getProperty("user.home").replace("\\", "/") + "/Desktop/Screenshots.zip");
            dl = new URL("http://ds-forums.com/kyle-tests/uploads/Screenshots.zip");
            OutputStream os = new FileOutputStream(fl);
            InputStream is = dl.openStream();
            CountingOutputStream count = new CountingOutputStream(os);
            dl.openConnection().getHeaderField("Content-Length");
            IOUtils.copy(is, os);//begin transfer

            os.close();//close streams
            is.close();//^
        } catch (Exception e) {
            System.out.println(e);
        }
    }
skaffman
  • 381,978
  • 94
  • 789
  • 754
Kyle
  • 2,830
  • 14
  • 49
  • 73

2 Answers2

13

If you are looking for a way to get the total number of bytes before downloading, you can obtain this value from the Content-Length header in http response.

If you just want the final number of bytes after the download, it is easiest to check the file size you just write to.

However if you want to display the current progress of how many bytes have been downloaded, you might want to extend apache CountingOutputStream to wrap the FileOutputStream so that everytime the write methods are called it counts the number of bytes passing through and update the progress bar.

Update

Here is a simple implementation of DownloadCountingOutputStream. I am not sure if you are familiar with using ActionListener or not but it is a useful class for implementing GUI.

public class DownloadCountingOutputStream extends CountingOutputStream {

    private ActionListener listener = null;

    public DownloadCountingOutputStream(OutputStream out) {
        super(out);
    }

    public void setListener(ActionListener listener) {
        this.listener = listener;
    }

    @Override
    protected void afterWrite(int n) throws IOException {
        super.afterWrite(n);
        if (listener != null) {
            listener.actionPerformed(new ActionEvent(this, 0, null));
        }
    }

}

This is the usage sample :

public class Downloader {

    private static class ProgressListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            // e.getSource() gives you the object of DownloadCountingOutputStream
            // because you set it in the overriden method, afterWrite().
            System.out.println("Downloaded bytes : " + ((DownloadCountingOutputStream) e.getSource()).getByteCount());
        }
    }

    public static void main(String[] args) {
        URL dl = null;
        File fl = null;
        String x = null;
        OutputStream os = null;
        InputStream is = null;
        ProgressListener progressListener = new ProgressListener();
        try {
            fl = new File(System.getProperty("user.home").replace("\\", "/") + "/Desktop/Screenshots.zip");
            dl = new URL("http://ds-forums.com/kyle-tests/uploads/Screenshots.zip");
            os = new FileOutputStream(fl);
            is = dl.openStream();

            DownloadCountingOutputStream dcount = new DownloadCountingOutputStream(os);
            dcount.setListener(progressListener);

            // this line give you the total length of source stream as a String.
            // you may want to convert to integer and store this value to
            // calculate percentage of the progression.
            dl.openConnection().getHeaderField("Content-Length");

            // begin transfer by writing to dcount, not os.
            IOUtils.copy(is, dcount);

        } catch (Exception e) {
            System.out.println(e);
        } finally {
            IOUtils.closeQuietly(os);
            IOUtils.closeQuietly(is);
        }
    }
}
Jochem
  • 25
  • 7
gigadot
  • 8,743
  • 7
  • 33
  • 49
  • How would I extend apache to use that? fl = new File(System.getProperty("user.home").replace("\\", "/") + "/Desktop/Screenshots.zip"); dl = new URL("http://ds-forums.com/kyle-tests/uploads/Screenshots.zip"); OutputStream os = new FileOutputStream(fl); InputStream is = dl.openStream(); CountingOutputStream count = new CountingOutputStream(os); dl.openConnection().getHeaderField("Content-Length"); IOUtils.copy(is, os);//begin transfer Am I doing it right? – Kyle Jan 15 '11 at 09:07
  • Would you mind appending the code above to your question? It is hard to read. I will try to help. – gigadot Jan 15 '11 at 09:13
  • @Kyle Updated. I hope you like the answer. – gigadot Jan 15 '11 at 10:16
  • BTW, dl.openConnection() will open a new connection as well as dl.openStream(). you should reuse the connection, not to open it again since you might get different content length if the content of target is generated dynamically. – gigadot Jan 15 '11 at 10:19
  • Wow thanks alot! I am impressed! I have a small question regarding the progress bar though. Do I set the value of the progress bar via progressBar.setValue? If so I'm having trouble accessing the downloaded bytes and turning that value into a percent from the content-length. I really hate to ask such dumb questions after you've made this nifty snippet. I appreciate it. (What I was trying to do is subtract content-length from downloaded bytes and just do math operators to get the % to set the progressbar. Is that the proper way?) – Kyle Jan 15 '11 at 15:30
  • You can add a setter method into `ProgressListener` to add a progressbar object. Alternaively, you may change the arguments of `ProgressListener` constructor to take a Progressbar object. In summary, you will need to pass the reference of the `Progressbar` object down to `ProgressListener` somehow. – gigadot Jan 15 '11 at 15:43
  • However, I think you need something like `SwingWorker` to make the progressbar work properly. Read Java tutorial here http://download.oracle.com/javase/tutorial/uiswing/components/progress.html I cannot remember how to do it on top of my head. You should look into previous stack overflow question and start a new thread if you have trouble. – gigadot Jan 15 '11 at 15:44
  • The whole reason why I introduce an `ActionListener` is because it will be used with `SwingWorker` and `ProgressBar` class. you should read up on Model-View-Controller (MVC) design pattern if you want to do GUI programming. It took me a month to realise that such a methodology exists. – gigadot Jan 15 '11 at 15:49
  • This throws `java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)` in `InputStream is = dl.openStream();` in my case. Any Ideas? – Mayuso Sep 19 '16 at 20:48
11

commons-io has IOUtils.copy(inputStream, outputStream). So:

OutputStream os = new FileOutputStream(fl);
InputStream is = dl.openStream();

IOUtils.copy(is, os);

And IOUtils.toByteArray(is) can be used to get the bytes.

Getting the total number of bytes is a different story. Streams don't give you any total - they can only give you what is currently available in the stream. But since it's a stream, it can have more coming.

That's why http has its special way of specifying the total number of bytes. It is in the response header Content-Length. So you'd have to call url.openConnection() and then call getHeaderField("Content-Length") on the URLConnection object. It will return the number of bytes as string. Then use Integer.parseInt(bytesString) and you'll get your total.

Bozho
  • 554,002
  • 136
  • 1,025
  • 1,121
  • Hmm.. is there a way to display the bytes downloaded from that stream? I'm looking but I don't see a way. Thanks for the reply. – Kyle Jan 15 '11 at 07:51
  • btw, the bytes themselves, or their count? – Bozho Jan 15 '11 at 07:54
  • the total bytes downloaded if that's what the count means? Sorry I'm still new to java. – Kyle Jan 15 '11 at 08:00
  • Fantastic. Thank you I hope I can get this working to display the current downloaded bytes in the label! – Kyle Jan 15 '11 at 08:06