Java实现HTTP多线程下载功能app

前言:你学过的每一样东西,你遭受的每一次苦难,都会在你一生中的某个时候派上用场。—— 菲茨杰拉德


转载请标明出处:http://blog.csdn.net/android_for_james/article/details/51476311

源码我放到了文章的末尾


先来看看软件运行效果:



Java实现HTTP多线程下载功能app_第1张图片

Java实现HTTP多线程下载功能app_第2张图片

一、我们先来谈谈什么是多线程下载

下载文件这个活动,通常我们会理解为像是我们在写作文,拿着笔从头写到结尾,当然这可以抽象成单线程下载的情况,而当这篇文章可以好多个人完成的时候,那我们可以分配这些人一人写一段,最终也写成一篇文章,当作文字数一定时,不难想象当然是多个人一起写更快一些。

因此我们可以知道多线程下载的原理就是:通常服务器同时与多个用户连接时,每个用户之间共享带宽。如果N个用户优先级相同那么每个用户连接到服务器上的实际带宽就是1/N,但是当我们启用多线程时我们可以得到的带宽就可以是x/N(x=1,2,3.....),当然这时下载的速度也就越快。

二、实现结构

1.多线程管理类(MultiThreadManager),它负责分配每个线程该从哪里下载,以及得到下载进度。

2.下载线程类(DownThreads),它负责实际的下载操作。

3.图形界面显示类(ViewInit),它负责显示图像界面。

4.监听器(OnClickListener),它负责监听按钮事件

三、核心内容

实现多线程下载的基础核心是在写入文件时,可以从任意位置写起,即我们使用RandomAccessFile来配合多线程来实现任意位置读写文件,我们可以这样理解:

这是普通流来写入文件时从头开始写入的过程


这是使用RandomAccessFile任意位置写入文件的过程:

Java实现HTTP多线程下载功能app_第3张图片

这样也就可以配合多线程从文件的多个位置下载并写入文件。

四、具体实现:

1、MultiThreadManager

public class MultiThreadManager {
	
	private String urlPath;
	private String filePath;
	
	private int threadNum;
	private int fileSize;
	
	private DownThreads[] threads;
	
	public MultiThreadManager(String urlPath,String filePath,int threadNum)
	{
		this.urlPath=urlPath;
		this.filePath=filePath;
		this.threadNum=threadNum;
		threads=new DownThreads[threadNum];
	}
	public void downLoad() throws Exception
	{
		URL url=new URL(urlPath);
		HttpURLConnection connection=(HttpURLConnection)url.openConnection();
		connection.setRequestMethod("GET");
		connection.setRequestProperty("Connection", "Keep-Alive");
		fileSize=connection.getContentLength();
		connection.disconnect();
		//分配每个线程需要在下载的文件块大小
		int currentPartSize=fileSize/threadNum+1;
		//要用RandomAccessFile来写入文件,因为它支持随机访问模式,即程序可以直接跳转至文件的任意位置来读写
		RandomAccessFile raFile=new RandomAccessFile(filePath,"rw");
		//记录要写入文件的大小
		raFile.setLength(fileSize);
		raFile.close();
		for(int i=0;i<threadNum;i++)
		{
			int startPosition=i*currentPartSize;
			RandomAccessFile currentPartFile=new RandomAccessFile(filePath,"rw");
			//将文件记录指针跳转至我们需要分割的位置
			currentPartFile.seek(startPosition);
			threads[i]=new DownThreads(startPosition,currentPartSize,currentPartFile,urlPath);
			threads[i].start();
		}	
	}
	//得到当前下载完成百分比,用于绘制进度条
	public int getCompleteRate()
	{
		int sumSize=0;
		for(int i=0;i<threadNum;i++)
		{
			sumSize+=threads[i].hasDownSize;
		}
		return (int) ((sumSize*1.0/fileSize)*100);
	}

}

2、DownThreads

public class DownThreads extends Thread{
	//下载线程
	public int hasDownSize;
	private int startPosition;
	private int currentPartSize;
	
	private RandomAccessFile currentPartFile;
	
	private String urlPath;

	public DownThreads(int startPosition,int currentPartSize,RandomAccessFile currentPartFile,String urlPath)
	{
		this.startPosition=startPosition;
		this.currentPartSize=currentPartSize;
		this.currentPartFile=currentPartFile;
		this.urlPath=urlPath;
	}
	public void run()
	{
		try {
			URL url=new URL(urlPath);
			HttpURLConnection connection=(HttpURLConnection)url.openConnection();
			connection.setRequestMethod("GET");
			InputStream in=connection.getInputStream();
			in.skip(this.startPosition);
			//设置缓冲区大小为4是为了提高运行速度
			byte[] buffer=new byte[4];
			int hasRead;
			while(hasDownSize<currentPartSize&&((hasRead=in.read(buffer))!=-1))
			{
				//写入文件操作
				currentPartFile.write(buffer, 0, hasRead);
				hasDownSize+=hasRead;
			}
			currentPartFile.close();
			in.close();
		   } catch (IOException e) {
			e.printStackTrace();
	       }
			
	}
}
3、 ViewInit
public class ViewInit extends JFrame{
	JTextField urlInfoText;
	JTextField filePathText;
	JTextField threadNumInfoText;
	JButton button;
	MyCommandListener listener;
	public ViewInit(){
		init();
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public void init()
	{
		setLayout(new FlowLayout());
		setBounds(300,300,700,150);
		urlInfoText=new JTextField(50);
		filePathText=new JTextField(50);
		threadNumInfoText=new JTextField(10);
		button=new JButton("确定");
		add(new JLabel("网址:"));
		add(urlInfoText);
		add(new JLabel("储存路径:"));
		add(filePathText);
		add(new JLabel("想要启动线程数:"));
		add(threadNumInfoText);
		add(button);	
	}
	
	public void setListener(MyCommandListener listener)
	{
		this.listener=listener;
		listener.setFilePath(filePathText);
		listener.setUrlInfo(urlInfoText);
		listener.setThreadNum(threadNumInfoText);
		button.addActionListener(listener);
	}

}

4、MyCommandListener接口

public interface MyCommandListener extends ActionListener{
	//设置资源路径,文件保存路径,线程数量
	public void setUrlInfo(JTextField urlText);
	public void setFilePath(JTextField fileText);
	public void setThreadNum(JTextField threadNum);

}
5、 OnClickListener

public class OnClickListener implements MyCommandListener{

	String urlPath=null;
	String filePath=null;
	int threadNum;
	JTextField urlText;
	JTextField filePathText;
	JTextField threadNumInfo;
	public void setUrlInfo(JTextField urlText)
	{
		this.urlText=urlText;
	}
	public void setFilePath(JTextField fileText)
	{
		this.filePathText=fileText;
	}
	public void setThreadNum(JTextField threadNum)
	{
		this.threadNumInfo=threadNum;
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		urlPath=urlText.getText();
		filePath=filePathText.getText();
		//获取所需线程数
		threadNum=Integer.parseInt(threadNumInfo.getText());
		//开启多线程下载
		MultiThreadManager mtDown=new MultiThreadManager(urlPath,filePath,threadNum);
		try {
			mtDown.downLoad();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		//创建进度对话框
		final ProgressMonitor dialog=new ProgressMonitor(null,"等待任务完成","已完成:",0,100);
		//在新线程中处理耗时任务
		new Thread(()->
		{
			//绘制进度条
			while(mtDown.getCompleteRate()<101)
			{
				dialog.setProgress(mtDown.getCompleteRate());
				if(dialog.isCanceled())
				{
					
					break;
				}
				try{
					Thread.sleep(300);
				}catch(Exception ex)
				{
					ex.printStackTrace();
				}
			}
		}).start();
	}

}

转载请标明出处:http://blog.csdn.net/android_for_james/article/details/51476311

源码下载地址:http://download.csdn.net/detail/android_for_james/9527888

如果对你有帮助,那就顶一下~~~

如果你喜欢我的文章欢迎关注我的博客:http://blog.csdn.net/android_for_james






你可能感兴趣的:(java,多线程,http,APP,多线程下载)