設計一個下載器

一、package DownLoader.ui 中的類:APPMain、DownLoaderJFrame、MyProgressBarjava

二、package DownLoader.utils 中類:ClimbNetPage、DownLoader、DownLoaderTask、ImageUtil、Speeder數組

三、package  DownLoaderTableMode 中的類:DownLoaderTableMode_Observer網絡

四、package MyDataBasic 中的類TestDataBasic多線程

 

package DownLoader.ui 中的APPMain類:
package DownLoader.ui;

import java.awt.Color;
import java.awt.Font;

import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * 主程序入口
 * @author Huangyujun
 *
 */
public class APPMain {
    public static void main(String[] args) {
        Font font1 = new Font("華文行楷", Font.BOLD, 18);
        Font font2 = new Font("微軟黑體", Font.BOLD, 18);
        //字體的修飾
        UIManager.put("TextField.font", font2);
        UIManager.put("Button.font", font1);
        UIManager.put("Table.font", font2);
        UIManager.put("TableHeader.font", font1);
        UIManager.put("TitledBorder.font", font1);
        //防止線程死鎖
        SwingUtilities.invokeLater(() ->
            new DownLoaderJFrame().setVisible(true)
        );
    }
}

 

package DownLoader.ui 中的DownLoaderJFrame類:
package DownLoader.ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;

import DownLoader.utils.ClimbNetPage;
import DownLoader.utils.DownLoader;
import DownLoader.utils.ImageUtil;
import DownLoaderTableMode.DownLoaderTableMode_Observer;

/**
 * 下載器窗體
 * 
 * @author Huangyujun
 *
 */
public class DownLoaderJFrame extends JFrame {
    // 控件:文本框控件、添加按鈕、表格、底部帶(暫停、重置、取消、清空)按鈕的控件
    private JTextField urlTxt = new JTextField(45);
    private JButton addButton = new JButton("添加");
    private JButton searchButton = new JButton("查詢");
    private JButton changeImageButton = null;    //換膚按鈕
    private JTable tableDownLoaderInfo = new JTable();
    private JButton pauseButton = new JButton("暫停");
    private JButton cancelButton = new JButton("取消");
    private JButton clearButton = new JButton("清空");
    private JButton climbButton = new JButton("爬網頁");
    private JPopupMenu popMenu = new JPopupMenu(); // 右鍵彈出菜單
    JMenuItem downloadMenuItem = new JMenuItem("下載"); // 下載菜單項
    JMenuItem delMenuItem = new JMenuItem("刪除"); // 刪除菜單項
    //表格模式
    DownLoaderTableMode_Observer tableMode_Observer = new DownLoaderTableMode_Observer();
    //圖片路徑數組
    private String[] imagePath = {null, 
        "D:\\EclispeProjects\\MyDownLoader\\photos\\xuehua.png",
        "D:\\EclispeProjects\\MyDownLoader\\photos\\flower.png",
        "D:\\EclispeProjects\\MyDownLoader\\photos\\shu.png"
    };
    int imageIndex;        //圖片數組下標
    int z;                //圖層在z軸上距離
    /** 換膚按鈕圖片 */
    private String changeImageButtonPath = "D:\\EclispeProjects\\MyDownLoader\\photos\\changeImaga.png";
    /** 靠近換膚按鈕圖片 */
    private String nearImageButtonPath = "D:\\EclispeProjects\\MyDownLoader\\photos\\nearImaga.png";
    /** 按下換膚按鈕圖片 */
    private String pressedImageButtonPath = "D:\\EclispeProjects\\MyDownLoader\\photos\\pressedImaga.png";
    JPanel contentPane = null;        // 內容面板
    public DownLoaderJFrame() {
        // 設置標題
        setTitle("一心下載");
        // 內容面板
        contentPane = (JPanel) getContentPane();
        //設置大小,與背景圖片同樣大
        setSize(1100, 700);    //下載器背景風格簡約,其中默認爲系統背景圖片
//        setSize(imageBackground.getIconWidth(), imageBackground.getIconHeight());
        //設置背景圖片
        setBackground(imagePath[0], 0);
        // 居中
        setLocationRelativeTo(null);
        // 封裝了其他初始化工做
        initComponents();
        // 事件
        initEvents();
        // 退出模式
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    /**
     * 設置自定義背景圖片
     */
    private void setBackground(String path, int z) {
        //獲取背景圖片
        ImageIcon imageIcon =  ImageUtil.getImage(path);
        //經過標籤控件封裝圖片標籤後,添加到佈局控件
        JLabel jLabelImage = new JLabel(imageIcon);
        //設置標籤邊界
        jLabelImage.setBounds(0, 0, imageIcon.getIconWidth(), imageIcon.getIconHeight());
        //添加至佈局面板
        JLayeredPane layoutPane = getLayeredPane();
        layoutPane.add(jLabelImage, new Integer(Integer.MIN_VALUE + z));
        //設置內容面板不透明爲假
        contentPane.setOpaque(false);
    }
    /**
     * 封裝了其他初始化工做
     */
    private void initComponents() {
        // 頂部流動佈局面板
        JPanel topPane = new JPanel();
        // 頂部面板添加文本框
        topPane.add(urlTxt);
        // 頂部面板添加添加按鈕
        topPane.add(addButton);
        // 頂部面板添加查詢按鈕
        topPane.add(searchButton);
        // 頂部面板添加換皮膚按鈕
        initChangeImage(topPane);
        // 中間下載信息面板,而且設置設置爲流式佈局
        JPanel middlePane = new JPanel(new BorderLayout());
        // 中間面板設置邊框,而且添加標題
        middlePane.setBorder(BorderFactory.createTitledBorder("下載內容"));
        // 中間面板添加帶表格的滾動面板
        middlePane.add(new JScrollPane(tableDownLoaderInfo));
        // 設置表格行高
        tableDownLoaderInfo.setRowHeight(30);
        // 菜單添加下載菜單項
        popMenu.add(downloadMenuItem);
        // 菜單添加刪除菜單項
        popMenu.add(delMenuItem);
        // 表格添加右鍵菜單
        tableDownLoaderInfo.add(popMenu);
        // 設置表格模式
        setTableMode(tableDownLoaderInfo);
        // 建立進度條對象
        MyProgressBar myProgressBar = new MyProgressBar();
        // 爲下載進度設置進度條
        tableDownLoaderInfo.getColumn("下載進度").setCellRenderer(myProgressBar);
        // 設置進度條的數字爲可見
        myProgressBar.setStringPainted(true);
        // 底部面板
        JPanel bottomPane = new JPanel();
        // 底部面板添加暫停按鈕、取消按鈕、清空按鈕、爬取按鈕
        bottomPane.add(pauseButton);
        bottomPane.add(cancelButton);
        bottomPane.add(clearButton);
        bottomPane.add(climbButton);
        //設置頂部佈局面板爲透明
        topPane.setOpaque(false);
        //設置中間面板爲透明
        middlePane.setOpaque(false);
        //設置底部面板爲透明
        bottomPane.setOpaque(false);
        // 內容面板添加頂部面板,而且設置位置爲頂部
        contentPane.add(topPane, BorderLayout.NORTH);
        // 內容面板添加中間面板,而且設置位置爲中間
        contentPane.add(middlePane, BorderLayout.CENTER);
        // 內容面板添加底部面板,而且設置位置爲底部
        contentPane.add(bottomPane, BorderLayout.SOUTH);
    }

    /**
     * 換皮膚
     * @param topPane
     */
    private void initChangeImage(JPanel topPane) {
        //添加按鈕
        changeImageButton = new JButton(new ImageIcon(changeImageButtonPath));
        //設置按鈕的空間大小
        changeImageButton.setPreferredSize(new Dimension(32, 32));
        //設置按鈕圖片,鼠標接近,按下的圖片切換
        changeImageButton.setRolloverIcon(new ImageIcon(nearImageButtonPath));
        changeImageButton.setPressedIcon(new ImageIcon(pressedImageButtonPath));
        //取消掉按鈕的邊框
        changeImageButton.setBorder(BorderFactory.createEmptyBorder());
        //爲按鈕添加換膚事件
        changeImageEvent();
        //添加按鈕到佈局面板
        topPane.add(changeImageButton);
    }
    /**
     * 換膚事件
     */
    private void changeImageEvent() {
        changeImageButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int imageIndex2 = imageIndex + 1;        //圖片數組下標
                if(imageIndex2 > 3) {
                    imageIndex = 0;
                    imageIndex2 = 1;
                }
                imageIndex++;
                z++;    //圖層在z軸上距離+1
                System.out.println(z);
                //設置更換圖片
                setBackground(imagePath[imageIndex2], z);    
            }
        }); 
        
    }

    /**
     * 事件
     */
    private void initEvents() {
        // 添加按鈕添加事件
        addButton.addActionListener(e -> addAction(e));
        // 查詢按鈕添加事件
        searchButton.addActionListener(e -> searchAction(e));
        // 暫停按鈕添加事件
        pauseButton.addActionListener(e -> pauseAction(e));
        // 取消按鈕添加事件
        cancelButton.addActionListener(e -> cancelAction(e));
        // 清空按鈕添加事件
        clearButton.addActionListener(e -> clearAction(e));
        // 爬取按鈕添加事件
        climbButton.addActionListener(e -> climbAction(e));
        // 爲表格添加右鍵菜單的事件
        tableDownLoaderInfo.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON3) { // 右鍵鼠標
                    popMenu.show(tableDownLoaderInfo, e.getX(), e.getY());
                }
            }
        });
        // 下載菜單項添加事件
        downloadMenuItem.addActionListener(e -> downloadAction(e));
        // 刪除菜單項添加事件
        delMenuItem.addActionListener(e -> cancelAction(e));

    }

    /**
     * 添加按鈕的事件行爲
     * 
     * @param e
     */
    private void addAction(ActionEvent e) {
        // 獲取文本框的內容
        String netUrl = urlTxt.getText();
        // 判斷內容是否合法
        if ("".equals(netUrl))
            return;
//        if(!netUrl.startsWith("https://") || netUrl.startsWith("http://")) {
//            //彈出對話框
//            JOptionPane.showMessageDialog(this, "本系統只支持Https協議!");
//            return;
//        }
        // 測試用的路徑
        String localPath = "D:\\test\\" + netUrl.substring(netUrl.lastIndexOf("\\") + 1);
//        //本地路徑
//        String localPath = netUrl.substring(netUrl.lastIndexOf("/") + 1);
        // 建立下載器對象
        DownLoader downloader = new DownLoader(netUrl, localPath, 5);
        // 調用表格的添加下載器的方法
        tableMode_Observer.addDownloader(downloader);
    }

    /**
     * 查詢事件行動
     * 
     * @param e
     */
    private void searchAction(ActionEvent e) {
        // 獲取輸入框文本
        String str = urlTxt.getText();
        // 調用表格模式的查詢方法
        tableMode_Observer.findStrDownLoader(str);
    }
    /**
     * 下載事件行動
     * @param e
     */
    private void downloadAction(ActionEvent e) {
        // 判斷是否選中了某一行
        int row = tableDownLoaderInfo.getSelectedRow();
        if (-1 == row)
            return;
        // 調用表格模式的下載方法
        tableMode_Observer.download(row);
    }

    /**
     * 添加暫停與恢復事件的行爲
     * 
     * @param e
     * @return
     */
    private void pauseAction(ActionEvent e) {
        // 判斷是否選中了某一行
        int row = tableDownLoaderInfo.getSelectedRow();
        if (-1 == row) {
            if ("暫停".equals(pauseButton.getActionCommand())) {
                pauseButton.setText("暫停");
                return;
            }
        } else {
            if ("暫停".equals(pauseButton.getActionCommand())) {
                pauseButton.setText("開始");
                // 調用表格模式中的暫停方法
                tableMode_Observer.pause(row);
            } else if ("開始".equals(pauseButton.getActionCommand())) {
                pauseButton.setText("暫停");
                // 調用表格模式中的恢復方法
                tableMode_Observer.resume(row);
            }
        }
    }

    /**
     * 取消--刪除選中的下載器事件行動
     * 
     * @param e
     */
    private void cancelAction(ActionEvent e) {
        // 判斷是否選中了某一行
        int row = tableDownLoaderInfo.getSelectedRow();
        if (-1 == row)
            return;
        // 調用表格模式中的取消刪除方法
        tableMode_Observer.deleteDownloader(row);
    }

    /**
     * 清空事件行動
     * 
     * @param e
     */
    private void clearAction(ActionEvent e) {
        // 調用表格模式中的清空方法
        tableMode_Observer.clearAllDownloader();
    }

    private void climbAction(ActionEvent e) {
        // 獲取文本框的內容
        String netUrl = urlTxt.getText();
        // 判斷內容是否合法
        if ("".equals(netUrl))
            return;
        if (!netUrl.startsWith("https://") || netUrl.startsWith("http://")) {
            // 彈出對話框
            JOptionPane.showMessageDialog(this, "本系統只支持Https協議!");
            return;
        }
        //本地路徑
        String localPath = netUrl.substring(netUrl.lastIndexOf("/") + 1);
        // 調用爬取類的爬取方法
        try {
            new ClimbNetPage(netUrl, localPath);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 設置表格模式
     * 
     * @param tableDownLoaderInfo2
     */
    private void setTableMode(JTable tableDownLoaderInfo2) {
        tableDownLoaderInfo2.setModel(tableMode_Observer);
    }
}

 

package DownLoader.ui 中的MyProgressBar類:
package DownLoader.ui;

import java.awt.Component;

import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

/**
 * 進度條渲染器類
 * @author Huangyujun
 *
 */
public class MyProgressBar extends JProgressBar implements TableCellRenderer{

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        //獲取單元格的值
        Float floatValue = Float.valueOf(value.toString());
        //設置單元格的值
        this.setValue(floatValue.intValue());
        return this;
    }

}

 

package DownLoader.utils 中的ClimbNetPage
package DownLoader.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 爬取網頁類--就直接實現下載靜態頁面的連接就能夠啦!
 * 
 * @author Huangyujun
 *
 */
public class ClimbNetPage {
    private String netUrl;
    private String localPath;
    public ClimbNetPage(String netUrl, String localPath) throws IOException {
        this.netUrl = netUrl;
        this.localPath = localPath;
        climb();
    }
    private void climb() throws IOException{
        File file = new File(localPath);
        if(!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream climbToFile = new FileOutputStream(file);
        // 網絡資源定位
        URL url = null;
        // 遠程對象
        HttpURLConnection urlConnection = null;
        // 輸入流
        InputStream inputStream = null;
        url = new URL(netUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        // 設置遠程對象的請求屬性和一些參數
        urlConnection.setRequestProperty("Range", "bytes=0-");
        // 發送鏈接請求
        urlConnection.connect();
        // 返回碼判斷鏈接是否成功
        int responCode = urlConnection.getResponseCode();
        if (responCode / 100 != 2) {
            // 返回碼錯誤
            System.err.println("鏈接失敗,返回碼:" + responCode);
        }
        // 獲取輸入流
        inputStream = urlConnection.getInputStream();
        byte[] buff = new byte[1024];
        int count = 0;
        while ((count = inputStream.read(buff)) != -1) {
            climbToFile.write(buff, 0, count);
        }
        System.out.println("爬取成功");
        inputStream.close();
        climbToFile.close();
    }
}

 

package DownLoader.utils 中的DownLoader
package DownLoader.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 下載器類--被觀察者
 * 
 * @author Huangyujun
 *
 */
public class DownLoader extends Observable {
    // 下載器的狀態
    private static int status;
    // 下載器狀態的常量
    /** 正在下載 */
    public static int DOWNLOAD = 0;
    /** 暫停 */
    public static int PAUSE = 1;
    /** 已完成 */
    public static int COMPLETE = 2;
    /** 取消 */
    public static int CANCEL = 3;
    /** 錯誤 */
    public static int WAIT = 4;
    // 下載狀態錯誤
    private static final String[] strStatus = { "正在下載", "暫停", "已完成", "取消", "等待下載" };
    // 下載器的工做是採起多線程下載網絡的文件
    private String netUrl = null;         // 網絡路徑
    private String localPath = null;     // 本地路徑
    private int countOfThread;             // 線程數量
    private int fileSize;                 // 文件大小
    private int partSize;                 // 文件分塊的大小
    private int downloadedBytes;         // 已經下載的字節數(算速度、進度)
    // 線程列表
    private List<DownLoaderTask> downLoaderTaskList = new ArrayList<DownLoaderTask>();
    // 速度計
    private Speeder speeder = null;
    // 速度
    private double speed;
    
    // 構造方法
    public DownLoader(String netUrl, String localPath, int countOfThread) {
        this.netUrl = netUrl;
        this.localPath = localPath;
        this.countOfThread = countOfThread;
        File file = new File(netUrl);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        // 文件大小
        fileSize = (int) file.length();
    }

    /**
     * 下載方法
     */
    public void download() {
        try {
            downloadTest();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * http協議的網絡路徑下載方法
     */
    private void httpDownload() {
        // 一樣是從網絡下載文件,而後分塊交給線程(下載任務)
        // 網絡資源定位
        URL url = null;
        // 遠程對象
        HttpURLConnection urlConnection = null;
        try {
            url = new URL(netUrl);
            urlConnection = (HttpURLConnection) url.openConnection();
            // 設置遠程對象的請求屬性和一些參數
            urlConnection.setRequestProperty("Range", "bytes=0-");
//            urlConnection.setRequestProperty("Accept-Encoding", "identity"); 
            // 發送鏈接請求
            urlConnection.connect();
            // 返回碼判斷鏈接是否成功
            int responCode = urlConnection.getResponseCode();
            if (responCode / 100 != 2) {
                // 返回碼錯誤
                throw new RuntimeException("鏈接失敗,返回碼:" + responCode);
            }
            // 鏈接成功後,獲取文件大小,以及分塊交給下載任務處理
            fileSize = urlConnection.getContentLength();
            // 按照線程數分塊的大小
            partSize = fileSize / countOfThread;
            // 隨機文件流
            RandomAccessFile randomFile = new RandomAccessFile(localPath, "rw");
            // 多線程
            ExecutorService executor = Executors.newFixedThreadPool(countOfThread);
            for (int i = 0; i < countOfThread; i++) {
                // 開始位置
                int startPositition = i * partSize;
                // 判斷最後一塊的大小
                if (i == countOfThread - 1 && fileSize % countOfThread != 0) {
                    partSize += fileSize % countOfThread;
                }
                //如何下載器狀態非下載,則跳出循環
                if(getStatus() != DOWNLOAD) {
                    return;
                }
                // 自動下載,設置下載器狀態爲下載
                setChanged(DOWNLOAD);
                // 實例化下載任務
                DownLoaderTask downloaderTask = new DownLoaderTask(netUrl, localPath, startPositition, partSize, randomFile,
                        this);
                // 把下載任務添加到任務列表裏
                downLoaderTaskList.add(downloaderTask);
                if(getStatus() == DOWNLOAD) {
                    // 啓動線程
                    executor.execute(downloaderTask);
                }
            }    
            // 開啓下載後啓動速度計線程
            speeder = new Speeder(this);
            speeder.start(); // 開啓速度計進程
        } catch (IOException e) {
            e.printStackTrace();
        } finally { // 關閉資源
            try {
                urlConnection.disconnect();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

    /**
     * 測試用的下載方法
     * @throws IOException 
     */
    private void downloadTest() throws IOException {
        // 按照線程數分塊的大小
        partSize = fileSize / countOfThread;
        // 隨機文件流
        RandomAccessFile randomFile = new RandomAccessFile(localPath, "rw");
        // 多線程
        ExecutorService executor = Executors.newFixedThreadPool(countOfThread);
        for (int i = 0; i < countOfThread; i++) {
            // 開始位置
            int startPositition = i * partSize;
            // 判斷最後一塊的大小
            if (i == countOfThread - 1 && fileSize % countOfThread != 0) {
                partSize += fileSize % countOfThread;
            }
            //如何下載器狀態非下載,則跳出循環
            if(getStatus() != DOWNLOAD) {
                return;
            }
            // 自動下載,設置下載器狀態爲下載
            setChanged(DOWNLOAD);
            // 實例化下載任務
            DownLoaderTask downloaderTask = new DownLoaderTask(netUrl, localPath, startPositition, partSize, randomFile,
                    this);
            // 把下載任務添加到任務列表裏
            downLoaderTaskList.add(downloaderTask);
            if(getStatus() == DOWNLOAD) {
                // 啓動線程
                executor.execute(downloaderTask);
            }
        }    
        // 開啓下載後啓動速度計線程
        speeder = new Speeder(this);
        speeder.start(); // 開啓速度計進程
    }
    
    /**
     * 暫停
     */
    public void pause() {
        setChanged(PAUSE);
    }
    
    /**
     * 恢復下載
     */
    public void resume() {
        this.setChanged(DOWNLOAD);
        this.download();
    }
    /**
     * 被觀察者最重要的功能是通知狀態
     * 
     * @param status
     */
    public void setChanged(int status) {
        this.status = status;
        this.setChanged();
        this.notifyObservers();
    }

    /**
     * 返回下載狀態
     * 
     * @return
     */
    public int getStatus() {
        return this.status;
    }

    /**
     * 返回網絡路徑
     * 
     * @return
     */
    public String getNetUrl() {
        return this.netUrl;
    }

    /**
     * 返回文件大小
     * 
     * @return
     */
    public int getFileSize() {
        return this.fileSize;
    }

    /**
     * 返回下載狀態
     * 
     * @return
     */
    public String getStrStaus() {
        return this.strStatus[this.getStatus()];
    }

    /**
     * 返回下載器下載的字節數量
     * 
     * @return
     */
    public int getDownloadedBytes() {
        this.downloadedBytes = 0;
        // 已下載字節數
        for (DownLoaderTask task : downLoaderTaskList) {
            this.downloadedBytes += task.getDownloadedBytes();
        }

        return this.downloadedBytes;
    }

    /**
     * 進度
     * 
     * @return
     */
    public double progress() {
        this.downloadedBytes = this.getDownloadedBytes();
        return downloadedBytes / 1.0 / fileSize;
    }

    /**
     * 返回速度計
     * 
     * @return
     */
    public Speeder getSpeeder() {
        return speeder;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + countOfThread;
        result = prime * result + ((localPath == null) ? 0 : localPath.hashCode());
        result = prime * result + ((netUrl == null) ? 0 : netUrl.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DownLoader other = (DownLoader) obj;
        if (countOfThread != other.countOfThread)
            return false;
        if (localPath == null) {
            if (other.localPath != null)
                return false;
        } else if (!localPath.equals(other.localPath))
            return false;
        if (netUrl == null) {
            if (other.netUrl != null)
                return false;
        } else if (!netUrl.equals(other.netUrl))
            return false;
        return true;
    }
    
    
    
}

 

package DownLoader.utils 中的DownLoaderTask
package DownLoader.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 下載任務類--線程
 * 
 * @author Huangyujun
 *
 */
public class DownLoaderTask implements Runnable {
    // 下載網絡文件塊
    private String netUrl = null; // 網絡路徑
    private String localPath = null; // 本地路徑
    private int startPosition; // 文件下載起始位置
    private int downloadedBytes; // 已經下載的字節數量
    private int partSize; // 文件塊的大小
    private RandomAccessFile randomFile; // 隨機文件流
    private DownLoader downloader; // 下載器對象屬性
    private static final int BYTE_SIZE = 1024 * 1024; // 分塊的字節大小--1M
    private static final int KNOWEDURL = 1;
    
    
    // 構造方法
    public DownLoaderTask(String netUrl, String localPath, int startPosition, int partSize, RandomAccessFile randomFile,
            DownLoader downloader) {
        this.netUrl = netUrl;
        this.localPath = localPath;
        this.startPosition = startPosition;
        this.partSize = partSize;
        this.randomFile = randomFile;
        this.downloader = downloader;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "下載開始");
        try {
            downloadTaskTest();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * http協議的下載任務線程方法
     */
    private void httpdownloadTaskTest() {
        // 網絡資源定位
        URL url = null;
        // 遠程對象
        HttpURLConnection urlConnection = null;
        // 輸入流
        InputStream inputStream = null;
        try {
            url = new URL(netUrl);
            urlConnection = (HttpURLConnection) url.openConnection();
            // 設置遠程對象的請求屬性和一些參數
            String range = String.format("bytes=%d-%d", startPosition, startPosition + partSize);
            urlConnection.setRequestProperty("Range", range);
            // 發送鏈接請求
            urlConnection.connect();
            // 返回碼判斷鏈接是否成功
            int responCode = urlConnection.getResponseCode();
            if (responCode / 100 != 2) {
                // 返回碼錯誤
                throw new RuntimeException("鏈接失敗,返回碼:" + responCode);
            }
            // 獲取輸入流
            inputStream = urlConnection.getInputStream();
            // 開始循環讀取--下載字節 < 文件塊的大小 和下載器的狀態是下載
            while (downloadedBytes < partSize && downloader.getStatus() == DownLoader.DOWNLOAD) {
                byte[] buff = null; // 字節數組
                // 爲了算速度,分塊
                if (partSize - downloadedBytes >= BYTE_SIZE) {
                    buff = new byte[BYTE_SIZE];
                } else {
                    buff = new byte[partSize - downloadedBytes];
                }
                // 當前的塊的讀取的字節數
                int currReadedBytes = 0;
                int read = -1;
                // 一個一個字節讀取
                while (currReadedBytes < buff.length && (read = inputStream.read()) != -1) {
                    // 讀取到字節數字裏
                    buff[currReadedBytes++] = (byte) read;
                    downloadedBytes++;
                }
                // 讀取到末尾
                if (read == -1) {
                    if (currReadedBytes != 0) {
                        // 寫入文件
                        randomFile.seek(startPosition);
                        randomFile.write(buff, 0, currReadedBytes);
                        break;
                    }
                }
                if (downloader.getStatus() != DownLoader.DOWNLOAD) {
                    return;
                }
                // 寫入文件
                randomFile.seek(startPosition);
                randomFile.write(buff);
                startPosition += buff.length;
            }
            if (downloadedBytes == partSize) {
                System.out.println(Thread.currentThread().getName() + "下載已經完成");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally { // 關閉資源
            try {
                inputStream.close();
                urlConnection.disconnect();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

    /**
     * 測試用的下載任務線程方法
     * 
     * @throws IOException
     */
    private void downloadTaskTest() throws IOException {
        File file = new File(netUrl);
        if (!file.exists()) {
            System.err.println("文件不存在!!!");
            return;
        }
        InputStream inputStream = new FileInputStream(file);
        // 開始循環讀取--下載字節 < 文件塊的大小 和下載器的狀態是下載
        while (downloadedBytes < partSize && downloader.getStatus() == DownLoader.DOWNLOAD) {
            byte[] buff = null; // 字節數組
            // 爲了算速度,分塊
            if (partSize - downloadedBytes >= BYTE_SIZE) {
                buff = new byte[BYTE_SIZE];
            } else {
                buff = new byte[partSize - downloadedBytes];
            }
            // 當前的塊的讀取的字節數
            int currReadedBytes = 0;
            int read = -1;
            // 一個一個字節讀取
            while (currReadedBytes < buff.length && (read = inputStream.read()) != -1) {
                // 讀取到字節數字裏
                buff[currReadedBytes++] = (byte) read;
                downloadedBytes++;
            }
            // 讀取到末尾
            if (read == -1) {
                if (currReadedBytes != 0) {
                    // 寫入文件
                    randomFile.seek(startPosition);
                    randomFile.write(buff, 0, currReadedBytes);
                    break;
                }
            }
            if (downloader.getStatus() != DownLoader.DOWNLOAD) {
                return;
            }
            // 寫入文件
            randomFile.seek(startPosition);
            randomFile.write(buff);
            startPosition += buff.length;
        }
        if (downloadedBytes == partSize) {
            System.out.println(Thread.currentThread().getName() + "下載已經完成");
        }
    }

    /**
     * 返回下載任務已經下載的字節數量
     * 
     * @return
     */
    public int getDownloadedBytes() {
        return downloadedBytes;
    }
}
 

 

 

package DownLoader.utils 中的ImageUtil
package DownLoader.utils;

import java.awt.Image;
import java.util.HashMap;
import java.util.Map;

import javax.swing.ImageIcon;

/**
 * 加載圖片工具類,實現加載過的圖片能夠下次直接獲取
 * @author Huangyujun
 *
 */
public class ImageUtil {
    //定義Map集合(鍵:圖片路徑, 值 圖片控件)
    public static Map<String, ImageIcon> mapImage = new HashMap<String, ImageIcon>();
    //獲取圖片控件
    public static ImageIcon getImage(String path) {
        //判斷圖片是否加載過
        if(mapImage.containsKey(path)) {    //加載過的圖片,就直接返回圖片控件
            return mapImage.get(path);
        }
        //沒有加載過的圖片
        ImageIcon image = new ImageIcon(path);
        mapImage.put(path, image);
        return image;
    }
    /**
     * 縮放圖片比例後,返回縮放後的圖片控件
     * @param image
     * @param scale
     */
    public static ImageIcon scaleImageIcon(Image image, double scale) {
        //調用Image控件的方法
        int newWidth = (int) (image.getWidth(null) * scale);
        int newHigt = (int) (image.getHeight(null) * scale);
        Image scaleImage = image.getScaledInstance(newWidth, newHigt, image.SCALE_DEFAULT);
        //圖標控件裏放圖片控件
        return new ImageIcon(scaleImage);
    }
}

 

package DownLoader.utils 中的Speeder
package DownLoader.utils;

/**
 * 速度計類
 * 
 * @author Huangyujun
 *
 */
public class Speeder extends Thread {
    // 對象屬性
    private DownLoader downloader;
    // 速度
    private double speed;

    // 構造方法
    public Speeder(DownLoader downloader) {
        this.downloader = downloader;
    }

    @Override
    public void run() {
        // 已經下載量
        int downloadedBytes = 0;
        while (true) {
            while (!Thread.currentThread().isInterrupted()) {
                if(downloader.getStatus() != DownLoader.DOWNLOAD) {
                    if(downloader.getStatus() == DownLoader.CANCEL) {
                        speed = 0;
                    }
                    break;
                }
                // 計算速度
                speed = (downloader.getDownloadedBytes() - downloadedBytes)  * 2;
                // 通知速度更新啦
                downloader.setChanged(DownLoader.DOWNLOAD);
                // 更新一下已經下載字節數
                downloadedBytes = downloader.getDownloadedBytes();
                // 判斷是否下載完成
                if (downloader.progress() >= 1.0) {
                    downloader.setChanged(DownLoader.COMPLETE);
                    downloader.getSpeeder().threadCancel();
                    break;
                }
                try {
                    // 休眠時間
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 線程阻斷
     */
    public void threadCancel() {
        interrupt();
    }

    /**
     * 返回速度
     * @return
     */
    public double getSpeed() {
        return this.speed;
    }
}
 

 

 

package DownLoaderTableMode 中的DownLoaderTableMode_Observer類
package DownLoaderTableMode;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JOptionPane;
import javax.swing.table.AbstractTableModel;

import DownLoader.utils.DownLoader;
import MyDataBasic.TestDataBasic;

/**
 * 表格模式類--觀察者
 * 
 * @author Huangyujun
 *
 */
public class DownLoaderTableMode_Observer extends AbstractTableModel implements Observer {
    // 設置表頭(列名)
    private static final String[] tableColumnName = { "連接", "文件大小(MB)", "下載進度", "下載速度", "下載狀態" };
    // 一張表格有多個下載器,一個下載器有多個下載任務
    private List<DownLoader> downloaderList = new ArrayList<DownLoader>();

    // 重寫表頭方法
    @Override
    public String getColumnName(int column) {
        return tableColumnName[column];
    }

    @Override
    public int getRowCount() {
        return downloaderList.size();
    }

    @Override
    public int getColumnCount() {
        return tableColumnName.length;
    }

    /**
     * 業務:增長下載器(被觀察者)
     * 
     * @param downloader
     */
    public void addDownloader(DownLoader downloader) {
        // 判斷表格中是否已經有了該下載器
        if (downloaderList.contains(downloader)) {
            JOptionPane.showMessageDialog(null, "已下載,請勿重複操做!");
            return;
        }
        //判斷當前下載器List集合是否有下載器在下載,有則彈出對話框提示用戶須要下載完成後再添加
        if(isPopDialog()) {
            JOptionPane.showMessageDialog(null, "請等待當前下載任務完成後添加!");
            return;
        }
        //設置下載器的狀態爲下載
        downloader.setChanged(DownLoader.DOWNLOAD);
        // 添加到表格下載器列表裏
        downloaderList.add(downloader);
        // 被觀察者添加觀察者,(觀察者與被觀察者創建聯繫)
        downloader.addObserver(this);
        // 添加下載器後自動開啓下載
        downloader.download();
        // 通知界面表格模式發生改變
        fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
    }
    
    /**
     * 是否彈出提示框
     * @return
     */
    private boolean isPopDialog() {
        //遍歷下載器List,判斷是否有下載器在下載
        for(int i = 0; i < downloaderList.size(); i++) {
            DownLoader downLoader = downloaderList.get(i);
            if(downLoader.getStatus() == DownLoader.DOWNLOAD) {
                return true;
            }
        }
        return false;
    }
    /**
     * 查找含某個字符串的下載器
     * @param str
     */
    public void findStrDownLoader(String str) {
        //含有str下載器的網絡路徑的下載器List
        List<DownLoader> strDownLoaderList = new ArrayList<DownLoader>();
        for(int i = 0; i < TestDataBasic.netUrl.length; i++) {
            if(TestDataBasic.netUrl[i].contains(str)) {
                DownLoader downLoader = TestDataBasic.downLoaderList.get(i);
                downLoader.setChanged(DownLoader.WAIT);
                strDownLoaderList.add(downLoader);
                //添加觀察者、被觀察的關係
                downLoader.addObserver(this);
            }
        }
        //當前下載器List等於找到的下載器List
        downloaderList = strDownLoaderList;
        //通知界面表格模式發生改變
        fireTableRowsInserted(0, getRowCount() - 1);
    }
    
    /**
     * 查詢後啓動下載---線程問題,因此只能先刪除"表面的下載器"--再添加下載
     * @param row
     */
    public void download(int row) {
        // 下載器須要刪除後再添加    
        DownLoader downloader = downloaderList.get(row);
        //清空表格全部的下載器,再添加下載器--由於原來的下載器致使的表格中的下載速度,
        //本來須要開啓線程,而以前是直接返回0,沒有涉及到線程,此刻添加下載器,又啓動了線程
        //這樣設計也符合我以前由於單線程計算下載速度計算兩個下載器速度時不許確,選擇一次只下載一個下載器
        clearAllDownloader();
        downloader.setChanged(DownLoader.DOWNLOAD);
        // 添加到表格下載器列表裏
        downloaderList.add(0, downloader);
        // 被觀察者添加觀察者,(觀察者與被觀察者創建聯繫)
        downloader.addObserver(this);
        // 添加下載器後自動開啓下載
        downloader.download();
        // 通知界面表格模式發生改變
        fireTableRowsInserted(0, 0);
    }
    
    /**
     * 根據下標添加下載器
     * @param row
     */
    public void addDownloader(DownLoader downLoader, int row) {
        //設置下載器的狀態爲下載
        downLoader.setChanged(DownLoader.DOWNLOAD);
        // 添加到表格下載器列表裏
        downloaderList.add(downLoader);
        // 被觀察者添加觀察者,(觀察者與被觀察者創建聯繫)
        downLoader.addObserver(this);
        // 添加下載器後自動開啓下載
        downLoader.download();
        // 通知界面表格模式發生改變
        fireTableRowsInserted(row, row);
    }
    
    /**
     * 暫停
     * 
     * @param row
     */
    public void pause(int row) {
        // 判斷當前是否有下載器
        if (downloaderList.isEmpty())
            return;
        // 判斷是哪個下載器須要暫停
        DownLoader pauseDownLoader = downloaderList.get(row);
        if (pauseDownLoader.getStatus() != DownLoader.PAUSE) {
            // 修改下載器的狀態
            pauseDownLoader.pause();
        }
        // 通知界面表格
        fireTableDataChanged();
    }
 
    /**
     * 恢復下載
     * @param row
     */
    public void resume(int row) {
        // 判斷當前是否有下載器
        if (downloaderList.isEmpty())
            return;
        // 判斷是哪個下載器須要暫停
        DownLoader resumeDownLoader = downloaderList.get(row);
        if (resumeDownLoader.getStatus() == DownLoader.PAUSE) {
            // 修改下載器的狀態
            resumeDownLoader.resume();
        }
        // 通知界面表格
        fireTableDataChanged();
    }
    
    /**
     * 取消--刪除選中的下載器
     * @param row
     */
    public void deleteDownloader(int row) {
        // 判斷是哪個下載器須要刪除
        DownLoader deleteDownLoader = downloaderList.get(row);
        if(deleteDownLoader.getStatus() != DownLoader.WAIT) {
            deleteDownLoader.setChanged(DownLoader.CANCEL);
        }
        if(deleteDownLoader.getStatus() == DownLoader.CANCEL) {
//            deleteDownLoader.getSpeeder().threadCancel();
        }
        downloaderList.remove(deleteDownLoader);
        // 被觀察者刪除觀察者
        deleteDownLoader.deleteObserver(this);
        // 通知界面表格模式發生改變
        fireTableRowsDeleted(row, row);
    }
    
    /**
     * 清空
     */
    public void clearAllDownloader() {
        int len = downloaderList.size();
        //不斷地調用刪除方法
        for(int i = 0; i < len; i++) {
            DownLoader deleteDownLoader = downloaderList.get(i);
            deleteDownLoader.setChanged(DownLoader.CANCEL);
            // 被觀察者刪除觀察者
            deleteDownLoader.deleteObserver(this);
        }
        downloaderList.clear();
        // 通知界面表格模式發生改變
        fireTableRowsDeleted(0, len);
    }
    
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        // 判斷表格是否有下載器
        if (downloaderList.isEmpty())
            return null;
        // 判斷是哪個下載器更新了
        DownLoader currDownloader = downloaderList.get(rowIndex);
        // 小數格式
        DecimalFormat format = new DecimalFormat("#.#");
        // 經過switch結構獲取屬性
        switch (columnIndex) {
        case 0: // 連接
            return currDownloader.getNetUrl();
        case 1:// 文件大小
            int fileSize = currDownloader.getFileSize();
            // 文件大小爲-1時的處理爲--
            if (fileSize == -1) {
                return "--";
            }
            return format.format(fileSize / 1024 / 1024.0) + "MB";
        case 2:// 下載進度=下載器已經下載的字節數/總文件大小
            return new Float(currDownloader.progress() * 100);
        case 3:// 下載速度
            if(currDownloader.getStatus() == DownLoader.WAIT) {
                return "0 MB/s";
            }else {
                return format.format(currDownloader.getSpeeder().getSpeed() / 1024 / 1024.0) + "MB/s";
            }    
        case 4:// 下載狀態
            return currDownloader.getStrStaus();
        }
        
        return null;
    }
    
    @Override
    public void update(Observable o, Object arg) {
        // 被觀察者(下載器)更新狀態後,須要通知界面層
        // 判斷表格是否有下載器
        if (downloaderList.isEmpty())
            return;
        // 判斷是哪個下載器更新了
        int newRow = downloaderList.indexOf(o);
        // 判斷是否選中下載器
        if (newRow == -1)
            return;
        // 通知界面層,表格行數據更新
        fireTableRowsUpdated(newRow, newRow);
    }

}

 

package MyDataBasic 中的類TestDataBasic
package MyDataBasic;

import java.util.ArrayList;
import java.util.List;

import DownLoader.utils.DownLoader;

public class TestDataBasic {
    public static List<DownLoader> downLoaderList = null;
    public static String[] netUrl = {
        "D:\\EclispeProjects\\MyDownLoader\\src\\sounds\\music.mp4",
        "D:\\EclispeProjects\\MyDownLoader\\src\\sounds\\car.mp4",
        "D:\\EclispeProjects\\MyDownLoader\\src\\sounds\\car2.mp4",
        "D:\\EclispeProjects\\MyDownLoader\\src\\sounds\\car3.mp4",
        "D:\\EclispeProjects\\MyDownLoader\\src\\sounds\\stop.mp4",
        "D:\\EclispeProjects\\MyDownLoader\\src\\sounds\\tiyu.mp4"
    };
    public static String[] localPath = new String[netUrl.length];
    static {
        downLoaderList = new ArrayList<DownLoader>();
        for(int i = 0; i < netUrl.length; i++) {
            localPath[i] = "D:\\test\\" + netUrl[i].substring(netUrl[i].lastIndexOf("\\") + 1);
            //建立和添加下載器對象
            DownLoader downloader = new DownLoader(netUrl[i], localPath[i], 5);
            downLoaderList.add(downloader);
        }
    }
}

 

另外整理一下出現的bug:因爲bug的出現以及爲了掩蓋bug,裏邊我加入了一些掩蓋bug代碼,我整理一下bug,小夥伴能夠不用個人方式改bug,由於個人方式更像一種岩石,經過加入判斷條件,把bug掩蓋掉了(bug整理放到我本身的評論裏)dom

參考自:老九學堂java線上班swing 第16章 的下載器案例ide

相關文章
相關標籤/搜索