Java学习 之 多线程下载文件【源码】

负责下载的线程:

 

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;


public class DownloadThread extends Thread {
	
	// 定义字节数组(用于取水的那个竹筒)的长度
	private final int BUFF_LEN = 100;
	// 定义下载的起始点
	private long start;
	// 定义下载的结束点
	private long end;
	// 下载资源对应的输入流
	private InputStream inputStream;
	// 下载资源对应的输出流
	private RandomAccessFile randomAccessFile;
	
	// 构造器:传入起始下载点,结束下载点,输入流,输出流
	public DownloadThread (long start,long end,InputStream inputStream,RandomAccessFile randomAccessFile) {
		// 打印一下该线程的起始下载点和结束下载点的位置信息
		System.out.println(start+" >------> "+end);
		this.start = start;
		this.end = end;
		this.inputStream = inputStream;
		this.randomAccessFile = randomAccessFile;
	}
	
	@Override
	public void run() {
		try {
			// 记录指针向前移动start个字符
			inputStream.skip(start);
			// 记录指针定位到start位置处
			randomAccessFile.seek(start);
			// 定义读取输入流内容的缓存数组(竹筒)
			byte[] buff = new byte[BUFF_LEN];
			// 本线程负责下载的资源大小
			long contentLen = end - start;
			// 定义最多需要几次就可以完成本线程的下载任务,方便控制线程的退出
			long readMaxTimes = contentLen/BUFF_LEN + 4;
			// 实际读取的字节数
			int reallyReadCount = 0;
			for (int i = 0; i < readMaxTimes; i++) {
				// 读取数据
				reallyReadCount = inputStream.read(buff);
				// 如果读取的字节数小于0,说明读取完毕,则退出循环
				if (reallyReadCount < 0) {
					break;
				}
				// 写入数据
				randomAccessFile.write(buff, 0, reallyReadCount);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 使用finally块来关闭当前线程的输入流和输出流	
			try {
				if (inputStream != null) {
					inputStream.close();
				}
				if (randomAccessFile != null) {
					randomAccessFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
		
}

 

 

 

传入Url地址,开启下载:

 

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MultiDownload {
	
	public static void main(String[] args) {
		// 定义开启的线程数
		final int DOWNLOAD_THREAD_NUM = 4;
		// 定义下载文件的文件名,包括后缀
		final String OUTPUT_FILE_NAME = "baidu.gif";
		// 定义一个DOWNLOAD_THREAD_NUM大小的输入流数组
		InputStream[] inArrays = new InputStream[DOWNLOAD_THREAD_NUM];
		// 定义一个DOWNLOAD_THREAD_NUM大小的输出流数组
		RandomAccessFile[] outArrays = new RandomAccessFile[DOWNLOAD_THREAD_NUM];
		
		try {
			// 创建一个URL对象,参数是我们要下载的资源的地址
			URL downloadUrl = new URL("http://www.baidu.com/img/baidu_sylogo1.gif");
			// 以该URL对象打开第一个输入流
			inArrays[0] = downloadUrl.openStream();
			// 获取该网络资源文件的长度
			long fileLength = getFileLength(downloadUrl);
			// 做一个打印
			System.out.println("该网络资源文件的大小:"+fileLength);
			// 以输出的文件名创建第一个输出流对象,模式是可读,可写
			outArrays[0] = new RandomAccessFile(OUTPUT_FILE_NAME, "rw");
			// 创建一个与下载资源文件相同大小的空文件
			for (int i = 0; i < fileLength; i++) {
				outArrays[0].write(0);
			}
			// 计算每个线程应该下载的字节数
			long everyThreadDownloadSize = fileLength/DOWNLOAD_THREAD_NUM;
			// 计算整个下载资源整除后剩下的余数
			long otherDownloadSize = fileLength%DOWNLOAD_THREAD_NUM;
			// 启动各个线程下载各自规定的读取长度的资源
			for (int i = 0; i < DOWNLOAD_THREAD_NUM; i++) {
				// 刚才只初始化了第一个输入流和输出流对象,初始化剩下的输入流和输出流对象
				if (i != 0) {
					inArrays[i] = downloadUrl.openStream();
					outArrays[i] = new RandomAccessFile(OUTPUT_FILE_NAME, "rw");
				}
				// 独立配置最后一个线程的下载参数(该线程负责下载整除后余下的资源)
				if (i == DOWNLOAD_THREAD_NUM -1) {
					new DownloadThread(i*everyThreadDownloadSize, (i+1)*everyThreadDownloadSize+otherDownloadSize, inArrays[i], outArrays[i]);
				} else {
					// 配置前几个线程的下载参数
					new DownloadThread(i*everyThreadDownloadSize, (i+1)*everyThreadDownloadSize, inArrays[i], outArrays[i]);
				}
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}
	
	// 根据URL获取该URL所指向的资源文件的长度
	private static long getFileLength(URL url) throws IOException{
		long length = 0;
		URLConnection urlConnection = url.openConnection();
		long size = urlConnection.getContentLength();
		length = size;
		return length;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值