the Art of java中的HTTP下载管理器

今天看了the Art of java中关于下载器的一章,感觉挺有趣,里面涉及到观察者模式。用了一个早上边理解边将代码打下来,添上注释。本人新手,注释中可能理解有偏差的地方,请各位指出

 

 

下载器分为4个类

 

<textarea cols="50" rows="15" name="code" class="java">package Downloader; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.Observable; public class Download extends Observable implements Runnable { private static final int MAX_BUFFER_SIZE = 1024;//最大缓冲长度 public static final String STATUSES[] = {"Downloading","Paused","Complete","Cancelled","Error"};//状态定义 public static final int DOWNLOADING = 0;//状态值 public static final int PAUSED = 1; public static final int COMPLETE = 2; public static final int CANCELLED = 3; public static final int ERROR = 4; private URL url;//下载连接 private int size;//下载文件大小 private int downloaded;//已经下载的大小 private int status;//状态量,对应上面的状态值 public Download(URL url)//初始化函数,由于是接受到一个URL时才新建一个Download线程,所以在初始化的时候直接导入下载链接 { this.url = url;//下载连接 size = -1;//初始大小-1,因为下载结束时InputStream返回-1,这里对应一下比较好。 downloaded = 0;//初始已下载量 status = DOWNLOADING;//因为新建Download类立即开始下载,所以这里将初始状态设为DOWNLOADING download();//启动线程 } public String getURL() { return url.toString();//因为URL地址可能被其它类引用,避免使用静态或者全局变量,这里用函数返回 } public int getSize() { return size;//原因同上 } public float getProgress() { return ((float)downloaded/size)*100;//将进程换成百分比,为进度条ProgressBar所用 } public int getStatus() { return status;//返回当时状态 } public void pause() { status = PAUSED;//改变状态 stateChanged();//通知观察者 } public void resume() { status = DOWNLOADING;//改变状态 stateChanged();//通知观察者 download();//重新开始下载,续传 } public void cancel() { status = CANCELLED;//改变状态 stateChanged();//通知观察者 } public void error() { status = ERROR;//改变状态 stateChanged();//通知观察者 } private void download() { new Thread(this).start();//实例化download类,启动线程 } private String getFileName(URL url) { String fileName = url.getFile();//从URl到String return fileName.substring(fileName.lastIndexOf('/')+1);//取最后一个&lsquo;/&rsquo;字符+1位后的字符,即*.mp3之类 } public void run() { RandomAccessFile file = null;//续传要求用RandomAccessfile,可以定位写入 InputStream stream = null;//写文件用 try { HttpURLConnection connection = (HttpURLConnection)url.openConnection();//生成connection实例,但此时并不连接 connection.setRequestProperty("Range", "bytes = " + downloaded + "-");//Property(所有权)。向服务器发送&ldquo;设置&rdquo;,这里将下载字节数设置为&ldquo;已经下载的&rdquo;到&ldquo;下载完为止&rdquo;。 connection.connect();//和服务器连接 if(connection.getResponseCode()/100 != 2)//连接成功,服务器应返回200这个数字 { error(); } int contentLength = connection.getContentLength();//返回下载文件地大小 if(contentLength &lt; 1)//如果文件少于1个字节,则出错 { error(); } if(size == -1)//如果等于size == -1则表明没有开始下载,所以将下载大小设为返回的文件大小 { size = contentLength; stateChanged();//通知观察者 } file = new RandomAccessFile(getFileName(url),"rw");//生成文件,'rw'表可读写 file.seek(downloaded);//寻找文件地末尾,作续传用。如果是新下载,则download = 0,即从文件头部开始 stream = connection.getInputStream();//或者服务器的输出流 while(status == DOWNLOADING)//还没下载完就不会退出循环 { byte[] buffer;//缓冲区 if(size - downloaded&gt;MAX_BUFFER_SIZE) { buffer = new byte[MAX_BUFFER_SIZE]; } else//如果还没下载的部分小于缓冲区(快下载完地时候),就将缓冲区设置为剩下的大小 { buffer = new byte[size - downloaded]; } int read = stream.read(buffer);//将流读到缓冲里 ,当缓冲区获取不到字节,即read不到东西时,返回-1 if(read == -1) break;//退出循环 file.write(buffer,0,read);//将缓冲区写入文件 downloaded += read;//已下载大小+缓冲区的字节数 stateChanged();//通知观察者。已下载了 } if(status == DOWNLOADING)//退出循环后检测 { status = COMPLETE;//标记为已完成 stateChanged();//通知观察者已完成 } }catch(Exception e) { error(); }finally { if(file != null)//关闭file { try { file.close(); }catch(Exception e) { e.printStackTrace(); } } if(stream != null) { try { stream.close(); }catch(Exception e) { e.printStackTrace(); } } } } private void stateChanged() { setChanged();//标记当前SUBJECT已经改变 notifyObservers();//通知所有注册地观察者,当前subject已经有所改变,要重新读数据了。 } }</textarea> 

 

<textarea cols="50" rows="15" name="code" class="java">package Downloader; import java.awt.Component; import javax.swing.JProgressBar; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; public class ProgressRenderer extends JProgressBar implements TableCellRenderer {//将ProgressBar嵌入表格单元中要实现接口TableCellRenderer public ProgressRenderer(int min,int max) { super(min,max);//进度条的最大和最小值 } public Component getTableCellRendererComponent(//当表格中的值改变时会首先执行这个函数 JTable table,Object value,boolean isSelected,boolean hasFocus,int row,int column) { setValue((int)((Float)value).floatValue());//Float is a Class!//改变进度条的值 return this; } }</textarea> 

 

 

<textarea cols="50" rows="15" name="code" class="java">package Downloader; import java.util.ArrayList; import java.util.Observable; import java.util.Observer; import javax.swing.JProgressBar; import javax.swing.table.AbstractTableModel; public class DownloadsTableModel extends AbstractTableModel implements Observer {//TableModel是Download的观察者,Download的情况要实时反映到Table上 //TableModel继承AbstractTableModel,它负责管理侦听器,并为生成 TableModelEvents ,它将管理并调度listeners private static final String[] columnNames = {"URL","Size","Progress","Status"};//列名 private static final Class[] columnClasses = {String.class,String.class,JProgressBar.class,String.class};//列值属性 private ArrayList downloadList = new ArrayList();//下载队列 public void addDownload(Download download)//将下载放在下载队列 { download.addObserver(this);//将TableModel注册成新增的下载的观察者 downloadList.add(download);//将下载放入下载队列 fireTableRowsInserted(getRowCount() - 1,getRowCount() - 1);//通知行地listener状态已改变。(这里first和last一样,表明只需要通知对应的行就可以了) } public Download getDownload(int row) { return(Download)downloadList.get(row);//返回某行的下载任务的引用 } public void clearDownload(int row) { downloadList.remove(row);//队列中删除某行的下载任务 fireTableRowsDeleted(row,row);//通知所有放有下载的行地listener状态已改变。 } public int getColumnCount() { return columnNames.length; } public String getColumnNames(int col) { return columnNames[col]; } public Class getColumnClass(int col) { return columnClasses[col]; } public int getRowCount() { return downloadList.size(); } public Object getValueAt(int row ,int col )//返回OBJECT类型,注意在接受时要转换 { Download download = (Download)downloadList.get(row);//取得某单元格地值 switch(col)//判断列值来返回单元格 { case 0: return download.getURL(); case 1: int size = download.getSize(); return (size == -1)? "" : Integer.toString(size);//没有下载时返回&ldquo;&rdquo;,有则返回下载文件大小 case 2: return new Float(download.getProgress());//返回下载进度,以百分比形式 case 3: return Download.STATUSES[download.getStatus()];// } return ""; } public void update(Observable o,Object arg)//当被观察者发生变化(接到被观察者通知)时,即执行这个函数 { int index = downloadList.indexOf(o);//o为传来消息的被观察者 fireTableRowsUpdated(index,index);//针对o通知对应的行(这里first和last一样,表明只需要通知对应的行就可以了) } } </textarea> 

 

<textarea cols="50" rows="15" name="code" class="c-sharp">package Downloader; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.URL; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; public class DownloadManager extends JFrame implements Observer {//DownloadManager也是一个观察者。 private JTextField addTextField; private DownloadsTableModel tableModel; private JTable table; private JButton pauseButton,resumeButton,cancelButton,clearButton; private Download selectedDownload;//当前选中的下载 private boolean clearing; public DownloadManager() { this.setTitle("Download Manager"); this.setSize(640,480); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { actionExit(); } }); JMenuBar menuBar = new JMenuBar();//菜单栏 JMenu fileMenu = new JMenu("File");//菜单 fileMenu.setMnemonic(KeyEvent.VK_F);//快捷键 JMenuItem fileExitMenuItem = new JMenuItem("Exit",KeyEvent.VK_X);//菜单项 fileExitMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionExit(); } }); fileMenu.add(fileExitMenuItem); menuBar.add(fileMenu); setJMenuBar(menuBar);//菜单栏加进JFrame JPanel addPanel = new JPanel(); addTextField = new JTextField(30); addPanel.add(addTextField); JButton addButton = new JButton("Add Download"); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionAdd();//新增下载并立即执行 } }); addPanel.add(addButton); tableModel = new DownloadsTableModel();//之前定义的tableModel类 table = new JTable(tableModel);//生成table table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { tableSelectionChanged();//当某行选中时执行这个函数 } }); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);//只可以选一行 ProgressRenderer renderer = new ProgressRenderer(0,100);//百分比0-100 renderer.setStringPainted(true);//显示字符百分比 table.setDefaultRenderer(JProgressBar.class, renderer); table.setRowHeight((int)renderer.getPreferredSize().getHeight()); JPanel downloadsPanel = new JPanel(); downloadsPanel.setBorder(BorderFactory.createTitledBorder("Downloads"));//生成边框 downloadsPanel.setLayout(new BorderLayout()); downloadsPanel.add(new JScrollPane(table),BorderLayout.CENTER); JPanel buttonsPanel = new JPanel(); pauseButton = new JButton("Pause"); pauseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionPause();//-----------------------------------一下是四个按钮和对应的功能 } }); pauseButton.setEnabled(false); buttonsPanel.add(pauseButton); resumeButton = new JButton("Resume"); resumeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionResume(); } }); resumeButton.setEnabled(false); buttonsPanel.add(resumeButton); cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionCancel(); } }); cancelButton.setEnabled(false); buttonsPanel.add(cancelButton); clearButton = new JButton("Clear"); clearButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionClear(); } }); clearButton.setEnabled(false); buttonsPanel.add(clearButton); getContentPane().setLayout(new BorderLayout()); getContentPane().add(addPanel,BorderLayout.NORTH); getContentPane().add(downloadsPanel,BorderLayout.CENTER); getContentPane().add(buttonsPanel,BorderLayout.SOUTH); } private void actionExit() { System.exit(0); } private void actionAdd() { URL verifiedUrl = verifyUrl(addTextField.getText());//检测URl是否合法,合法则根据输入生成URL if(verifiedUrl != null) { tableModel.addDownload(new Download(verifiedUrl));//启动URL下载并加入下载队列 addTextField.setText("");//清空URL输入框 } else { JOptionPane.showMessageDialog(this, "Invalid Download URL","Error",JOptionPane.ERROR_MESSAGE); } } private URL verifyUrl(String url)//检测URL是否合法,不要在生成DOWNLOAD后再检查。 { if(!url.toLowerCase().startsWith("http://")) return null; URL verifiedUrl = null; try { verifiedUrl = new URL(url); }catch(Exception e) { return null; } if(verifiedUrl.getFile().length() &lt; 2) { return null; } return verifiedUrl; } private void tableSelectionChanged()// { if(selectedDownload != null) { selectedDownload.deleteObserver(DownloadManager.this);//取消对selectedDownload的观察 } if(!clearing) { selectedDownload = tableModel.getDownload(table.getSelectedRow()); selectedDownload.addObserver(DownloadManager.this);//增加对selectedDownload的观察 updateButtons();//刷新按钮情况; } } private void actionPause() { selectedDownload.pause(); updateButtons(); } private void actionResume() { selectedDownload.resume(); updateButtons(); } private void actionCancel() { selectedDownload.cancel(); updateButtons(); } private void actionClear() { clearing = true; tableModel.clearDownload(table.getSelectedRow()); clearing = false; selectedDownload = null; updateButtons(); } private void updateButtons() { if(selectedDownload != null) { int status = selectedDownload.getStatus(); switch (status) { case Download.DOWNLOADING: pauseButton.setEnabled(true); resumeButton.setEnabled(false); cancelButton.setEnabled(true); clearButton.setEnabled(false); break; case Download.PAUSED: pauseButton.setEnabled(false); resumeButton.setEnabled(true); cancelButton.setEnabled(true); clearButton.setEnabled(false); break; case Download.ERROR: pauseButton.setEnabled(false); resumeButton.setEnabled(true); cancelButton.setEnabled(false); clearButton.setEnabled(true); break; default: pauseButton.setEnabled(false); resumeButton.setEnabled(false); cancelButton.setEnabled(false); clearButton.setEnabled(true); break; } }else { pauseButton.setEnabled(false); resumeButton.setEnabled(false); cancelButton.setEnabled(false); clearButton.setEnabled(false); } } public void update(Observable o,Object arg) { if(selectedDownload != null &amp;&amp; selectedDownload.equals(o)) updateButtons(); } public static void main(String[] args) { DownloadManager manager = new DownloadManager(); manager.setVisible(true); } } </textarea> 

 

 

 

你可能感兴趣的:(java,exception,String,url,buffer,download)