Java多线程下载文件

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

 

/**

* @author EX-QINCIDONG001 多线程下载文件。 思想:开启5个线程,每个线程下载文件大小的1/5,<br>

* 每个线程生成的文件以文件名_编号作为文件名。<br>

* 每个线程结束后都检查自己是否是最后一个线程。<br>

* 如果是最后一个线程,就启动合并文件的线程<br>

* 用RandomAccessFile类,有追加文件的方法。<br>

* 扫描所有的文件,合并为一个文件。

*

* 例:<br>

* Thread File Name File Size

* Thread-1 test_1.mp3 300kb

* Thread-2 test_2.mp3 300kb

* Thread-3 test_3.mp3 200kb

*

* 最终的文件:test.mp3

*/

publicclass ThreadDownloadFile {

// 要下载的文件的URL

private String fileUrl;

// 要保存的文件名

private String saveName;

// 要创建的线程数量

privatestaticfinalintTHREAD_COUNT = 5;

// 保存线程运行的状态(0标识线程还在运行,1标识结束)

Map<Integer, Integer> threadStatusMap = new HashMap<Integer, Integer>();

 

public ThreadDownloadFile(String fileUrl,String saveName) {

this.fileUrl = fileUrl;

this.saveName = saveName;

// 初始化线程运行状态

for (int i=0;i<THREAD_COUNT;i++) {

// key:线程编号,value:线程运行状态

threadStatusMap.put(i, 0);

}

}

 

privatevoid download() throws IOException {

URL url = new URL(this.fileUrl);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setAllowUserInteraction(true);

conn.connect();

// 文件总的长度

int contentLength = conn.getContentLength();

// 每个线程应该分配的长度

int partLength = contentLength / this.THREAD_COUNT + 1;

 

conn.disconnect();

 

System.out.println("开始下载文件...");

 

for (int i = 0; i < this.THREAD_COUNT; i++) {

int length = partLength;

if (i == this.THREAD_COUNT - 1) { // 最后一个的长度

length = contentLength - partLength * i;

}

int index1 = saveName.lastIndexOf("/");

int index2 = saveName.lastIndexOf(".");

String partFileName = saveName.substring(0, index1+1)

+ saveName.substring(index1+1, index2) + "_"+(i + 1)

+ saveName.substring(index2, saveName.length());

DownloadThread dt = new DownloadThread(conn,contentLength,this.THREAD_COUNT,i,length,i*length, url, partFileName,threadStatusMap,this.saveName);

dt.start();

}

}

 

/**

* @param args

*/

publicstaticvoid main(String[] args) {

// String saveName = "http://www.a.b/c/d.mp3";

// int index1 = saveName.lastIndexOf("/");

// int index2 = saveName.lastIndexOf(".");

// int i = 0;

// String partFileName = saveName.substring(0, index1+1)

// + saveName.substring(index1+1, index2) +"_"+ (i + 1)

// + saveName.substring(index2, saveName.length());

// System.out.println(partFileName);

String fileUrl = "http://www.baidu.com";//"http://localhost:8080/filedownload/download1.jsp";

String saveName = "D:/Users/ex-qincidong001/Desktop/outlook邮件归类.docx";

ThreadDownloadFile tdf = new ThreadDownloadFile(fileUrl,saveName);

try {

tdf.download();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

 

}

 

class DownloadThread extends Thread {

// 总长度

privateintcontentLength;

// 线程数

privateintthreadNum;

// 当前线程编号

privateintcurrentThreadNum;

privateintlength;

privateintoffset;

private URL url;

private String partFileName;

// 要保存的文件名

private String saveName;

private Map<Integer,Integer> threadStatusMap;

HttpURLConnection conn;

 

DownloadThread(HttpURLConnection conn,int contentLength,int threadNum,int currentThreadNum,int length,int offset, URL url, String partFileName,Map<Integer,Integer> threadStatusMap,String saveName) {

this.conn = conn;

this.contentLength = contentLength;

this.threadNum = threadNum;

this.currentThreadNum = currentThreadNum;

this.length = length;

this.offset = offset;

this.url = url;

this.partFileName = partFileName;

this.threadStatusMap = threadStatusMap;

this.saveName = saveName;

}

 

publicvoid run() {

System.out.println("线程【" +this.currentThreadNum + "】开启...");

// HttpURLConnection conn;

try {

conn = (HttpURLConnection) url.openConnection();

conn.setAllowUserInteraction(true);

conn.setRequestProperty("RANGE", "bytes=" + offset );

conn.connect();

// 设置断点续传的开始位置

 

FileOutputStream fos = new FileOutputStream(partFileName);

InputStream is = conn.getInputStream();

byte[] data = newbyte[1024];

int len = -1;

int readSize = 0;

 

while ((len = is.read(data)) != -1) {

readSize += len;

 

if (readSize > length) { // 只读取length长度

len = len - (readSize - length);

}

fos.write(data, 0, len);

 

if (readSize > length) {

break;

}

}

 

fos.flush();

is.close();

// 将线程运行状态改为1(结束)

this.threadStatusMap.remove(currentThreadNum);

this.threadStatusMap.put(currentThreadNum, 1);

 

// 检查是否全部想线程都运行完毕

boolean flag = true;

Set<Integer> keys = this.threadStatusMap.keySet();

Iterator<Integer> its = keys.iterator();

while (its.hasNext()) {

Integer key = its.next();

Integer value = this.threadStatusMap.get(key);

if (value == 0) {

flag = false;

break;

}

}

 

System.out.println("线程【" +this.currentThreadNum + "】结束...");

 

if (flag) { // 所有的下载线程均结束,可以开始合并文件的线程

MergeThread mt = new MergeThread(this.contentLength,this.threadNum,this.saveName);

mt.start();

System.out.println("文件下载完毕...\n文件保存位置:" + this.saveName);

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

 

}

}

 

/**

* 合并文件的线程。

* @author EX-QINCIDONG001

*

*/

class MergeThread extends Thread {

// 文件总长度

privateintcontentLength;

// 线程的数量,以便于知道,生成了几个子文件。

privateintthreadNum;

// 要保存的最终的文件名

private String saveName;

 

public MergeThread(int contentLength,int threadNum,String saveName) {

this.contentLength = contentLength;

this.threadNum = threadNum;

this.saveName = saveName;

}

publicvoid run() {

int index1 = saveName.lastIndexOf("/");

int index2 = saveName.lastIndexOf(".");

RandomAccessFile accessFile = null;

try {

accessFile = new RandomAccessFile(saveName,"rw");

} catch (FileNotFoundException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

// 每个线程应该分配的长度

int partLength = contentLength / this.threadNum + 1;

for (int i=0;i<threadNum;i++) {

// 子文件文件名

String partFileName = saveName.substring(0, index1+1)

+ saveName.substring(index1+1, index2) +"_"+ (i + 1)

+ saveName.substring(index2, saveName.length());

 

int length = partLength;

if (i == this.threadNum - 1) { // 最后一个的长度

length = contentLength - partLength * i;

}

 

try {

accessFile.seek(i*length);

FileInputStream fis = new FileInputStream(partFileName);

byte[] data = newbyte[1024];

int len = -1;

 

while ((len = fis.read(data)) != -1) {

accessFile.write(data,0,len);

}

 

fis.close();

accessFile.close();

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值