前言

因为大作业要做浏览器,浏览器要带下载器,所以现在先实现一个带界面的多线程下载器类。

多线程下载器原理

之前写过一个的【Java URLConnection类 实现多线程下载文件】,只是那个demo比较简陋。。。

今天我们来加上图形界面,其实下载部分的代码都是复用过来的。。。。

JFrame

主窗体,windows窗口,可缩放等等

JTextField

文字框输入,我们需要用户输入或者改变

  1. 下载的url
  2. 保存地址
  3. 文件名

可以通过.set / get Text方法设置或者取得里面的文字

JButton

按钮组件,我们点击后,开始创建下载线程并且开始下载。

可以简单的为一个JButton对象绑定被点击时候的事件

// 开始下载按钮 注册事件 点击的时候创建新下载线程JButton startBtn = new JButton("开始下载");startBtn.addActionListener(new ActionListener() {    public void actionPerformed(ActionEvent e) {        // 按钮按下的时候执行此处代码    }});

JLabel

一个文字显示的组件,可以支持显示html格式,我们用于显示一些提示信息

JProgressBar

这次的重点,进度条组件,使用方便,在构造函数的时候,指定最小值,最大值

JProgressBar bar = new JProgressBar(0, 100);

然后可以通过.setValue来改变当前进度条的值

布局

Javaswing带界面和进度条的多线程下载器实现插图

JFrame使用【网格布局】,通过以下代码就可以实现一个9行1列的网格

// 主窗体JFrame jf = new JFrame("下载器");jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);jf.setSize(512, 321);jf.setLayout(new GridLayout(9, 1));

实现

因为进度条需要调用组件,所以我们创建一个类叫MultiThreadDownloader,将所有组件都设置为成员变量

此外我们还要存一个下载进程,方便我们查看下载进度并且更新进度条,因为所有下载进程共享一个进度,我们保留其中一个下载进程,就可以轻松获取了。

public class MultiThreadDownloader {        String url;             // 目标url    String savePath;        // 本地保存路径    String fileName;        // 文件名    int tnum = 5;           // 默认线程数目    long totalLength;       // 总大小    DownloadThread dth;     // 一个下载线程,用于查看下载进度    JLabel  urlInputText;   // 文字:目标对象url    JTextField urlInput;    // url输入框    JLabel  pathInputText;  // 文字:本地保存路径    JTextField pathInput;   // 保存路径输入框    JLabel  nameInputText;  // 文字:文件名    JTextField nameInput;   // 文件名输入框    JLabel  barTex;         // 文字:进度条    JProgressBar bar;       // 进度条    JButton startBtn;       // 开始下载按钮    ExecutorService pool = Executors.newCachedThreadPool(); // 线程池        MultiThreadDownloader() {            }        MultiThreadDownloader(String url, String savePath, String fileName, int tnum) {        this.url = url;        this.tnum = tnum;        this.savePath = savePath;        this.fileName = fileName;    }...下面是类的其他方法实现}

我们编写一个download方法,该方法初始化我们的界面并且显示,除此之外,还为按钮注册按下事件, 事件内容里面执行的代码就是创建下载线程并且开始下载。

public void download() throws Exception {            // 初始化组件,利用默认内容    // url输入    urlInputText = new JLabel ("目标对象URL");    urlInput = new JTextField(url);    // 路径输入    pathInputText = new JLabel ("本地保存路径");    pathInput = new JTextField(savePath);    // 文件名输入    nameInputText = new JLabel ("文件名");    nameInput = new JTextField(fileName);    // 进度条    JLabel  barText = new JLabel ("进度条");    bar = new JProgressBar(0, 100);        // 开始下载按钮 注册事件 点击的时候创建新下载线程    startBtn = new JButton("开始下载");    startBtn.addActionListener(new ActionListener() {        public void actionPerformed(ActionEvent e) {            startBtn.setEnabled(false); // 按钮禁用            // 获得用户实际输入的内容            savePath = pathInput.getText();            fileName = nameInput.getText();            url = urlInput.getText();            try {                totalLength = new URL(url).openConnection().getContentLength();                bar.setMaximum((int)totalLength);                long eachLength = totalLength / tnum;                // 拓展名                String ext = url.substring(url.lastIndexOf("."));                // 创建下载线程                for(int i=0; i<tnum; i++) {                         long st = eachLength * i;                    long ed = eachLength * (i+1);                    if(i == tnum-1) {                        ed=Math.max(ed, totalLength);                    }                    // 建立URL连接                    URLConnection con = new URL(url).openConnection();                    con.setRequestProperty("Range", "bytes="+String.valueOf(st)+"-"+String.valueOf(ed));                    con.connect();                    // 打开文件流                    RandomAccessFile rf = new RandomAccessFile(savePath+fileName+ext, "rw");                    rf.seek(st);    // 文件流跳转到对应位置                    // 创建下载线程                    DownloadThread d =new DownloadThread(con, rf);                    pool.submit(d);                        // 保存最后一个线程 方便查看进度                    if(i==tnum-1) {                        dth = d;                    }                }                // 创建一个每500ms更新进度条的线程并且跑起来                pool.submit(new Thread() {                    public void run() {                        while(true) {                            bar.setValue((int)dth.allcur);                            if(dth.allcur >= totalLength) {                                startBtn.setText("下载完成");                                break;                            }                            try {                                this.sleep(500);                            } catch (InterruptedException e) {                                e.printStackTrace();                            }                        }                    }                });                // 关闭线程池                pool.shutdown();            } catch(Exception e1) {                e1.printStackTrace();            }        }    });    // 开始下载按钮 注册事件 -- 结束        // 主窗体    JFrame jf = new JFrame("下载器");    jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);    jf.setSize(512, 321);    jf.setLayout(new GridLayout(9, 1));    jf.add(urlInputText);    jf.add(urlInput);    jf.add(pathInputText);    jf.add(pathInput);    jf.add(nameInputText);    jf.add(nameInput);    jf.add(barText);    jf.add(bar);    jf.add(startBtn);    jf.show();}

main函数只需创建相应的下载器,调用download方法即可。

这里我在我的个人网址上提供了两个链接用于测试下载,分别是稍大的pdf文件和小型txt文件。

http://www.szulrl.cn/browserTest/PDFFILE.zip
http://www.szulrl.cn/browserTest/TXTFILE.zip

public static void main(String[] args) throws Exception {    MultiThreadDownloader dl = new MultiThreadDownloader(            "http://www.szulrl.cn/browserTest/PDFFILE.zip",             "E:/MyEclipse/WorkSpace/Hello/src/homework/",             "下载",            3);    dl.download();}

完整代码

/* * @Author : @李若龙 2018171028 @SZU @CSSE */import java.net.*;import java.util.*;import java.util.concurrent.*;import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.*;import javax.swing.*;import javax.swing.event.*;import javax.swing.text.html.*;class DownloadThread extends Thread {        URLConnection con;    RandomAccessFile rf;        public static volatile long allcur = 0;    public static boolean isReady = false;        public DownloadThread() {            }        public DownloadThread(URLConnection con, RandomAccessFile rf) {        this.con = con;        this.rf = rf;    }        public void run() {        try {            // 获取输入输出流            InputStream is = con.getInputStream();            //System.out.printf("线程 获取文件成功\n");                        // 输入流内容写入输出流            byte[] buf = new byte[1024];            int len = -1;                        while((len=is.read(buf)) != -1) {                rf.write(buf, 0, len);                synchronized(new Object()) {                    allcur += (long)len;                }                // System.out.printf("%d / %d \n", allcur, 29210163);            }            rf.close();                    } catch(Exception e) {            e.printStackTrace();        }    }}public class MultiThreadDownloader {        String url;             // 目标url    String savePath;        // 本地保存路径    String fileName;        // 文件名    int tnum = 5;           // 默认线程数目    long totalLength;       // 总大小    DownloadThread dth;     // 一个下载线程,用于查看下载进度    JLabel  urlInputText;   // 文字:目标对象url    JTextField urlInput;    // url输入框    JLabel  pathInputText;  // 文字:本地保存路径    JTextField pathInput;   // 保存路径输入框    JLabel  nameInputText;  // 文字:文件名    JTextField nameInput;   // 文件名输入框    JLabel  barTex;         // 文字:进度条    JProgressBar bar;       // 进度条    JButton startBtn;       // 开始下载按钮    ExecutorService pool = Executors.newCachedThreadPool(); // 线程池        MultiThreadDownloader() {            }        MultiThreadDownloader(String url, String savePath, String fileName, int tnum) {        this.url = url;        this.tnum = tnum;        this.savePath = savePath;        this.fileName = fileName;    }        public void download() throws Exception {                // 初始化组件,利用默认内容        // url输入        urlInputText = new JLabel ("目标对象URL");        urlInput = new JTextField(url);        // 路径输入        pathInputText = new JLabel ("本地保存路径");        pathInput = new JTextField(savePath);        // 文件名输入        nameInputText = new JLabel ("文件名");        nameInput = new JTextField(fileName);        // 进度条        JLabel  barText = new JLabel ("进度条");        bar = new JProgressBar(0, 100);                // 开始下载按钮 注册事件 点击的时候创建新下载线程        startBtn = new JButton("开始下载");        startBtn.addActionListener(new ActionListener() {            public void actionPerformed(ActionEvent e) {                startBtn.setEnabled(false); // 按钮禁用                // 获得用户实际输入的内容                savePath = pathInput.getText();                fileName = nameInput.getText();                url = urlInput.getText();                try {                    totalLength = new URL(url).openConnection().getContentLength();                    bar.setMaximum((int)totalLength);                    long eachLength = totalLength / tnum;                    // 拓展名                    String ext = url.substring(url.lastIndexOf("."));                    // 创建下载线程                    for(int i=0; i<tnum; i++) {                             long st = eachLength * i;                        long ed = eachLength * (i+1);                        if(i == tnum-1) {                            ed=Math.max(ed, totalLength);                        }                        // 建立URL连接                        URLConnection con = new URL(url).openConnection();                        con.setRequestProperty("Range", "bytes="+String.valueOf(st)+"-"+String.valueOf(ed));                        con.connect();                        // 打开文件流                        RandomAccessFile rf = new RandomAccessFile(savePath+fileName+ext, "rw");                        rf.seek(st);    // 文件流跳转到对应位置                        // 创建下载线程                        DownloadThread d =new DownloadThread(con, rf);                        pool.submit(d);                        if(i==tnum-1) {                            dth = d;                        }                    }                    // 创建一个每500ms更新进度条的线程                    pool.submit(new Thread() {                        public void run() {                            while(true) {                                //System.out.printf("%d / %d \n", dth.allcur, totalLength);                                bar.setValue((int)dth.allcur);                                if(dth.allcur >= totalLength) {                                    startBtn.setText("下载完成");                                    break;                                }                                try {                                    this.sleep(500);                                } catch (InterruptedException e) {                                    e.printStackTrace();                                }                            }                        }                    });                    // 关闭线程池                    pool.shutdown();                } catch(Exception e1) {                    e1.printStackTrace();                }            }        });        // 开始下载按钮 注册事件 点击的时候创建新下载线程 -- 结束                // 主窗体        JFrame jf = new JFrame("下载器");        jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);        jf.setSize(512, 321);        jf.setLayout(new GridLayout(9, 1));        jf.add(urlInputText);        jf.add(urlInput);        jf.add(pathInputText);        jf.add(pathInput);        jf.add(nameInputText);        jf.add(nameInput);        jf.add(barText);        jf.add(bar);        jf.add(startBtn);        jf.show();            }        public static void main(String[] args) throws Exception {        MultiThreadDownloader dl = new MultiThreadDownloader(                "http://www.szulrl.cn/browserTest/PDFFILE.zip",                 "E:/MyEclipse/WorkSpace/Hello/src/homework/",                 "下载",                3);        dl.download();    }}

来源:
https://mp.weixin.qq.com/s/DTnch_ibXswXOXNvZyWb4A