Java 仿迅雷下载文件

概要:

很多人喜欢用下载工具下载电影或其他音乐,其中迅雷就是一款很好的多线程下载工具。本实例介绍运用多线程制作一个简单的仿迅雷的下载工具来下载文件。

| |目录

技术要点

制作一仿迅雷下载工具来下载文件的技术要点如下:

  • 多线程下载工具,可以并发下载同一个文件,每个线程下载文件的一部分,最后再组合成一个完整的文件。在实现多线程下载时,在下载、线程启动时记录或给予一定的日志信息或提示。

  • 在下载工具中设置目标文件的路径和下载文件的另存路径,设置一个区域显示下载文件时线程的信息以及其他相关内容。

代码实现

FileSplit.java

package net.xsoftlab.baike;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.swing.JTextArea;
public class FileSplit extends Thread {
	String downloadURL;// 下载文件的地址
	long startPosition;// 线程的开始位置
	long endPosition;// 线程的结束位置
	int threadID;
	JTextArea textArea = new JTextArea();// 创建文本域
	boolean isDone = false;// 是否下载完毕
	RandomAccessFile random;
	public FileSplit(String downloadURL, String saveAs, long nStart, long nEnd,
			int id, JTextArea textArea) {
		this.downloadURL = downloadURL;
		this.startPosition = nStart;
		this.endPosition = nEnd;
		this.threadID = id;
		this.textArea = textArea;
		try {
			random = new RandomAccessFile(saveAs, "rw");// 创建随机访问对象,以读/写方式
			random.seek(startPosition);// 定位文件指针到startPosition位置
		} catch (Exception e) {// 捕获异常
			System.out.println("创建随机访问对象出错:" + e.getMessage());
		}
	}
	public void run() {// 实现Thread类的方法
		try {
			URL url = new URL(downloadURL);// 根据网址创建URL对象
			HttpURLConnection httpConnection = (HttpURLConnection) url
					.openConnection();// 创建远程对象连接对象
			String sProperty = "bytes=" + startPosition + "-";
			httpConnection.setRequestProperty("RANGE", sProperty);
			textArea.append("
 线程" + threadID + "下载文件!  请等待...");
			InputStream input = httpConnection.getInputStream();// 获得输入流对象
			byte[] buf = new byte[1024];// 创建字节数据存储文件的数据
			int splitSpace;
			splitSpace = (int) endPosition - (int) startPosition;// 获得每个线程的间隔
			if (splitSpace > 1024)
				splitSpace = 1024;
			while (input.read(buf, 0, splitSpace) > 0
					&& startPosition < endPosition) {// 读取文件信息
				splitSpace = (int) endPosition - (int) startPosition;
				if (splitSpace > 1024)
					splitSpace = 1024;
				textArea.append("
线程: " + threadID + " 开始位置: " + startPosition
						+ ",  间隔长度: " + splitSpace);
				random.write(buf, 0, splitSpace);// 写入文件
				startPosition += splitSpace;// 开始位置改变
			}
			textArea.append("
 线程" + threadID + "下载完毕!!");
			random.close();// 释放资源
			input.close();
			isDone = true;
		} catch (Exception e) {// 捕获异常
			System.out.println("多线程下载文件出错:" + e.getMessage());
		}
	}
}

DownLoadFile.java

package net.xsoftlab.baike;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.swing.JTextArea;
public class DownLoadFile extends Thread {// 分析下载的文件并启动下载进程
	String downloadURL;// 下载文件的地址
	String saveFileAs;// 文件另存为
	int threadCount;// 线程总数
	String log = new String();// 下载过程的日志记录
	JTextArea textArea = new JTextArea();// 创建文本域
	long[] position;
	long[] startPosition;// 每个线程开始位置
	long[] endPosition;// 每个线程结束位置
	FileSplit[] FileSplitt; // 子线程对象
	long fileLength;// 下载的文件的长度
	public DownLoadFile(String downloadURL, String saveFileAs, int threadCount,
			JTextArea textArea) {// 构造方法进行初始化
		this.downloadURL = downloadURL;
		this.saveFileAs = saveFileAs;
		this.threadCount = threadCount;
		this.textArea = textArea;
		startPosition = new long[threadCount];
		endPosition = new long[threadCount];
	}
	public void run() {// 实现Thread类的方法
		log = "目标文件: " + downloadURL;
		textArea.append("
" + log);// 日志写入文本域
		log = "
 线程总数: " + threadCount;
		textArea.append("
" + log);
		try {
			fileLength = getFileSize();// 获得文件长度
			if (fileLength == -1) {// 不可获取文件长度或没有找到资源
				textArea.append("
 不可知的文件长度!请重试!!");
			} else {
				if (fileLength == -2) {// 无法获取文件或没有找到资源
					textArea.append("
 文件无法获取,没有找到指定资源,请重试!!");
				} else {
					for (int i = 0; i < startPosition.length; i++) {// 循环对每个线程的开始位置赋值
						startPosition[i] = (long) (i * (fileLength / startPosition.length));
					}
					for (int i = 0; i < endPosition.length - 1; i++)
						// 循环对每个线程的结束位置赋值
						endPosition[i] = startPosition[i + 1];
					endPosition[endPosition.length - 1] = fileLength;// 最后一个线程的结束位置是文件的长度
					for (int i = 0; i < startPosition.length; i++) {// 循环显示每个线程的开始和结束位置
						log = "线程:" + i + "下载范围:" + startPosition[i] + "--"
								+ endPosition[i];
						textArea.append("
" + log);
					}
					FileSplitt = new FileSplit[startPosition.length];
					for (int i = 0; i < startPosition.length; i++) {// 启动一组子线程
						FileSplitt[i] = new FileSplit(downloadURL, saveFileAs,
								startPosition[i], endPosition[i], i, textArea);
						log = "线程 " + i + "启动";
						textArea.append("
" + log);
						FileSplitt[i].start();// 启动线程
					}
					boolean breakWhile = true;
					while (breakWhile) {// 当条件始终为true时进行循环
						Thread.sleep(500);// 线程休眠
						breakWhile = false;
						for (int i = 0; i < FileSplitt.length; i++) {
							if (!FileSplitt[i].isDone) {// 循环判断每个线程是否结束
								breakWhile = true;
								break;
							}
						}
					}
					textArea.append("
 文件传输结束!");// 文件传输结束
				}
			}
		} catch (Exception ex) {// 捕获异常
			ex.printStackTrace();
		}
	}
	public long getFileSize() {// 获得文件的长度的方法
		int fileLength = -1;
		try {
			URL url = new URL(downloadURL);// 根据网址创建URL对象
			HttpURLConnection httpConnection = (HttpURLConnection) (url
					.openConnection());// 创建远程对象连接对象
			int responseCode = httpConnection.getResponseCode();
			if (responseCode >= 400) {// 没有获得响应信息
				System.out.println("Web服务器响应错误");
				return -2;// Web服务器响应错误
			}
			String sHeader;
			for (int i = 1;; i++) { // 查找标识文件长度的文件头,获取文件长度
				sHeader = httpConnection.getHeaderFieldKey(i);
				if (sHeader != null) {
					if (sHeader.equals("Content-Length")) {// 查找标识文件长度的文件头
						fileLength = Integer.parseInt(httpConnection
								.getHeaderField(sHeader));
						break;
					}
				} else {
					break;
				}
			}
		} catch (Exception e) {// 捕获异常
			System.out.println("无法获得文件长度:" + e.getMessage());
		}
		return fileLength;
	}
}

XunleiFrame.java

package net.xsoftlab.baike;

import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

//模拟迅雷下载的界面面板
public class XunleiFrame extends JFrame {
	private static final long serialVersionUID = -1783153163903658922L;

	private JPanel contentPane;// 迅雷面板
	private JTextField webField = new JTextField();// 下载地址的文本框
	private JTextField localFile = new JTextField();// 下载到本地的文本框
	private JButton button = new JButton();// 下载按钮
	private JLabel webLabel = new JLabel();// 目标标签
	private JLabel localLabel = new JLabel();// 下载到本地标签
	private JTextArea textArea = new JTextArea();// 显示下载记录的文本域
	private String downloadURL = new String();// 下载地址
	private String saveFileAs = new String();// 另存为

	public XunleiFrame() {// 构造方法进行初始化
		enableEvents(AWTEvent.WINDOW_EVENT_MASK);
		try {
			toInit();// 调用方法初始化面板
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void toInit() throws Exception {// 初始化面板
		contentPane = (JPanel) this.getContentPane();// 创建面板
		contentPane.setLayout(null);// 没有设置面板布局
		this.setSize(new Dimension(380, 320));// 面板的大小
		this.setLocation(100, 100);// 面板位置
		this.setTitle("仿迅雷多线程下载");// 面板标题
		webField.setBounds(new Rectangle(150, 200, 200, 20));// 设置文本框的位置
		// 设置默认下载路径
		webField.setText("http://image.xsoftlab.net/wx.jpg");
		localFile.setBounds(new Rectangle(150, 240, 120, 20));// 设置文本框的位置
		localFile.setText("d:\\wx.jpg");// 设置默认另存为
		webLabel.setBounds(new Rectangle(20, 200, 120, 20));// 标签的位置
		webLabel.setText("下载的目标文件为: ");
		localLabel.setBounds(new Rectangle(20, 240, 120, 20));// 标签的位置
		localLabel.setText("下载的文件另存为: ");
		button.setBounds(new Rectangle(280, 240, 60, 20));// 按钮的位置
		button.setText("下载");
		button.addActionListener(new ActionListener() {// 按钮添加监听事件
			public void actionPerformed(ActionEvent e) {
				button_actionPerformed(e);// 调用事件
			}
		});
		JScrollPane scrollPane = new JScrollPane(textArea);// 创建有滑动条的面板将文本域放在上面
		scrollPane.setBounds(new Rectangle(20, 20, 330, 170));// 面板的位置
		textArea.setEditable(false);// 不可编辑
		contentPane.add(webField, null);// 将文本框添加到面板中
		contentPane.add(localFile, null);// 将文本框添加到面板中
		contentPane.add(webLabel, null);// 将标签添加到面板中
		contentPane.add(localLabel, null);// 将标签添加到面板中
		contentPane.add(button, null);// 将按钮添加到面板中
		contentPane.add(scrollPane, null);// 将滑动条添加到面板中
		downloadURL = webField.getText();// 获得文本框中的文本
		saveFileAs = localFile.getText();
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);// 设置默认关闭操作
	}

	public void button_actionPerformed(ActionEvent e) {// 点击事件触发方法,启动分析下载文件的进程
		downloadURL = webField.getText();// 获得目标文件的网址
		saveFileAs = localFile.getText();// 获得另存为的地址
		if (downloadURL.compareTo("") == 0)
			textArea.setText("请输入要下载的文件完整地址");
		else if (saveFileAs.compareTo("") == 0) {
			textArea.setText("请输入保存文件完整地址");
		} else {
			try {
				DownLoadFile downFile = new DownLoadFile(downloadURL, saveFileAs, 5, textArea);// 传入参数实例化下载文件对象
				downFile.start();// 启动下载文件的线程
				textArea.append("主线程启动...");
			} catch (Exception ec) {// 捕获异常
				System.out.println("下载文件出错:" + ec.getMessage());
			}
		}
	}

	public static void main(String[] args) {// java程序主入口处
		XunleiFrame frame = new XunleiFrame();// 实例化对象进行初始化
		frame.setVisible(true);// 设置窗口可视
	}
}

程序解读

  1. FileSplit类继承Thread线程类扩展run()方法。类的构造方法中获得目标文件的路径、线程的开始和结束位置以及获得线程的编号和文本域。以读/写的方式创建随机访问对象并设置读取文件的指针位置。

  2. FileSplit类的run()方法根据目标文件的路径创建URL对象,其openConnection()方法创建远程连接对象。setRequestProperty()方法设置断点续传的开始位置。getInputStream()方法获得输入流。创建字节数组用来存储文件的数据信息。根据输入流中读取的数据大于0以及开始位置小于结束位置进行循环,在文本域中添加线程的处理信息,并根据随机访问对象的write()方法将读取的内容写入文件。写入完毕后释放相关流资源。

  3. DownLoadFile类继承Thread类扩展run()方法。在其构造方法中获得目标文件的路径、下载文件的另存为路径、线程的个数、文本域以及创建线程开始与结束位置数组。

  4. DownLoadFile类的run()方法,调用getFileSize()方法获得目标文件的长度。如果长度为-1,则表明文件的长度不可知;如果长度为-2,则表明无法获得资源文件。根据开始与结束位置的数组长度循环对每个线程的开始与结束位置进行赋值,最后一个线程的结束位置的长度为文件的长度。再运用循环显示每个线程的开始与结束位置,并将这些信息写入文本域中。根据线程开始位置的数组的长度创建FileSplit对象数组。循环对对象数组的每个子元素进行实例化,其每个子元素实际上是一个线程,启动线程。利用标识为真进行循环,查看每个子线程是否执行完毕,如果子线程执行完毕则跳出循环。

  5. DownLoadFile类的getFileSize()方法用来获得目标文件的长度。根据目标文件路径创建URL对象。其openConnection()方法创建远程连接对象。getResponseCode()方法获得响应的信息,如果响应的值超过400则不能获得Web服务器的信息,即不能获得目标文件资源。getHeaderFieldKey()方法获取文件头部信息。如果文件头部信息为Content-Length,则根据getHeaderField()方法获得相应文件的长度。

  6. XunleiFrame类继承JFrame类实现仿迅雷的界面。其构造方法中enableEvents()方法启用由传递给此组件的指定事件屏蔽参数所定义的事件。WINDOW_EVENT_MASK参数用于选择窗口事件的事件掩码。调用toInit()方法初始化界面面板。

  7. XunleiFrame类的toInit()方法创建面板并设置面板的布局为空,这样可以在面板中随意地摆放组件。设置默认的下载地址与默认的另存的文件路径。创建下载按钮并对按钮添加监听器。当单击按钮时会触发button_actionPerformed()方法。创建滑动条面板并将文本域放置在滑动面板上,当文本域中的内容超过设置的行数时会出现滑动条。文本域设置为不可编辑。

  8. button_actionPerformed()方法获得目标文件的路径和另存为的路径并判断它们是否为空或空字符串。如果都不是空或空字符串,则根据目标文件路径与另存为路径以及相关参数创建DownLoadFile对象,由于DownLoadFile继承Thread线程类,则其为一个线程,启动线程开始下载文件。文本域中显示文件下载过程的信息。


评论关闭
评论 还能输入200
评论关闭
评论 还能输入200
  • 全部评论(0)
资料加载中...
已关注 , 取消