使用Java Apache Commons下载文件?

13

我该如何使用这个库下载文件并打印出保存的字节数?我尝试使用

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

但是我无法显示字节或进度条,应该使用哪种方法?

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);
        }
    }
2个回答

13

如果您想获取下载前的总字节数,可以从http响应的Content-Length头中获取此值。

如果您只想获取下载后的最终字节数,则最简单的方法是检查您刚写入的文件大小。

但是,如果您想显示已下载的字节数的当前进度,您可能希望扩展apache CountingOutputStream来包装FileOutputStream,以便每次调用write方法时都计算通过的字节数并更新进度条。

更新

这里是DownloadCountingOutputStream的简单实现。我不确定您是否熟悉使用ActionListener,但它是一种实现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));
        }
    }

}

以下是用法示例:

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

您能否将上面的代码附加到您的问题中?这样更容易阅读。我会尽力帮助您。 - gigadot
@Kyle 已更新。希望您喜欢这个答案。 - gigadot
顺便提一下,dl.openConnection() 会打开一个新的连接,就像 dl.openStream() 一样。你应该重复使用这个连接,而不是再次打开它,因为如果目标内容是动态生成的,你可能会得到不同的内容长度。 - gigadot
哇,非常感谢!我印象深刻!不过我有一个关于进度条的小问题。我是通过progressBar.setValue来设置进度条的值吗?如果是这样,我遇到了一些问题,无法访问已下载的字节数,并将该值转换为从内容长度计算出的百分比。在您提供这个巧妙的代码片段之后,我真的很不想问这样愚蠢的问题。我很感激。(我试图做的是将内容长度减去已下载的字节数,并执行数学运算以获取设置进度条的百分比。这是正确的方法吗?) - Kyle
然而,我认为你需要像SwingWorker这样的东西才能使进度条正常工作。在这里阅读Java教程http://download.oracle.com/javase/tutorial/uiswing/components/progress.html。我无法立即想起如何做。如果遇到问题,您应该查看以前的堆栈溢出问题并启动新线程。 - gigadot
显示剩余4条评论

11

commons-io提供了IOUtils.copy(inputStream, outputStream)方法。所以:

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

IOUtils.copy(is, os);

IOUtils.toByteArray(is)可以用来获得字节。

获取总字节数是另一回事。流无法提供总数,它们只能提供流中当前可用的内容。但因为它是一个流,所以可能还有更多的内容要来。

这就是为什么HTTP有其特殊的指定总字节数的方式。它在响应头中Content-Length中指定。所以你需要调用url.openConnection(),然后在URLConnection对象上调用getHeaderField("Content-Length")。它将返回字节数作为字符串。接着使用Integer.parseInt(bytesString)即可得到总数。


嗯...有没有一种方法可以显示从该流下载的字节数?我在找,但我没有看到一种方法。谢谢回复。 - Kyle
顺便问一下,是字节本身还是它们的数量? - Bozho
如果计数器的意思是下载的总字节数,那么它表示下载的总字节数。抱歉,我还是Java的新手。 - Kyle
太棒了。谢谢!我希望我能让它工作,以便在标签中显示当前下载的字节数! - Kyle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接