`
Luob.
  • 浏览: 1592306 次
  • 来自: 上海
社区版块
存档分类
最新评论

Java 多线程断点续传 (基于HTTP)

阅读更多
http://wenku.baidu.com/view/57fd6d8002d276a200292eca.html



package com.itheima.net.socket;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 多线程断点续传  基于 (HTTP)
 * @author Bin
 *  Windows NT 6.1
 */
public class TextThreadsAndPointAdd {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String webAddr="http://nchc.dl.sourceforge.net/project/jtidy/JTidy/r938/jtidy-r938.zip";
		String fileDir="F:/temp";
		String fileName="ss.zip";
		int count=5;
		new TextThreadsAndPointAdd(webAddr,fileDir,fileName,count);
				

	}
	
	public TextThreadsAndPointAdd(String webAddr,String fileDir,String fileName,int count) {
		try {
			TranBean bean=new TranBean(webAddr,fileDir,fileName,count);
			ControlFileFetch fileFetch=new ControlFileFetch(bean);
			fileFetch.start();
		} catch (Exception e) {
			System.out.println("多线程下载文件出错:"+e.getMessage());
			System.exit(1);
		}
	}
}

//扩展多线程类,负责文件的抓取,控制内部线程
class ControlFileFetch extends Thread{
	TranBean tranBean=null; 			//扩展信息bean
	long [] startPosition;				//开始位置
	long [] endPosition;				//结束位置
	FileFetch[] childThread;			//子线程对象
	long fileLength;					//文件长度
	boolean isFitstGet=true;			//是否第一次去文件
	boolean isStopGet=false;			//停止标志
	File fileName;						//文件下载的临时信息
	DataOutputStream output;			//输出到文件的输出流
	
	public ControlFileFetch(TranBean tranBean){
		this.tranBean=tranBean;
		fileName=new File(tranBean.getFileDir()+File.separator+tranBean.getFileName()+".info");  //创建文件
		System.out.println(tranBean.getFileDir()+File.separator+tranBean.getFileName()+".info");
		if(fileName.exists()){
			isFitstGet=false;
			readInfo();
		}else{
			startPosition=new long[tranBean.getCount()];
			endPosition =new long[tranBean.getCount()];
		}
		
	}
	
	public void run(){
		try {
			if(isFitstGet){								//第一次读取文件
				fileLength=getFieldSize();				//调用方法获取文件长度
				
				if(fileLength==-1){ 					
					System.err.println("文件长度为止");
				}else if(fileLength==-2){
					System.err.println("不能访问文件");
				}else{
					System.out.println("文件的长度:"+fileLength);
					
					//循环划分 每个线程要下载的文件的开始位置
					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;
				}
			}
			//创建 子线程数量的数组
			childThread=new FileFetch[startPosition.length];
			
			for (int i = 0; i < startPosition.length; i++) {
				childThread[i]=new FileFetch(tranBean.getWebAddr(), tranBean.getFileDir()+File.separator+tranBean.getFileName(), startPosition[i], endPosition[i], i);
				Log.log("线程"+(i+1)+",的开始位置="+startPosition[i]+",结束位置="+endPosition[i]);
				childThread[i].start();
			}
			
			boolean breakWhile=false;
			while (!isStopGet) {
				savePosition();
				Log.sleep(500);//	
				breakWhile=true;
				for (int i = 0; i < startPosition.length; i++) { //循环实现下载文件
					if(!childThread[i].downLoadOver){
						breakWhile=false;
						break;
					}
				}
				if(breakWhile)
					break;
				
			}
			System.err.println("文件下载结束!");
			
		} catch (Exception e) {
			System.out.println("下载文件出错:"+e.getMessage());
		}
		
	}
	
	//保存下载信息(文件指针信息)
	private void savePosition() {
		try {
			output=new DataOutputStream(new FileOutputStream(fileName));
			output.writeInt(startPosition.length);
			for (int i = 0; i < startPosition.length; i++) {
				output.writeLong(childThread[i].startPosition);
				output.writeLong(childThread[i].endPosition);
			}
			output.close();
		} catch (Exception e) {
			System.out.println("保存下载信息出错:"+e.getMessage());
		}
		
		
	}

	//获得文件的长度
	public long getFieldSize(){
		int fileLength=-1;
		try {
			URL url=new URL(tranBean.getWebAddr()); 	//根据网址传入网址创建URL对象
			//打开连接对象
			HttpURLConnection httpConnection=(HttpURLConnection)url.openConnection();
			//设置描述发出HTTP请求的终端信息
			httpConnection.setRequestProperty("User-Agent", "NetFox");
			int responseCode=httpConnection.getResponseCode(); 
			//表示不能访问文件
			if(responseCode>=400){
				errorCode(responseCode);
				return -2;
			}
			String head;
			for (int i = 1; ; i++) {
				head=httpConnection.getHeaderFieldKey(i); //获取文件头部信息
				if(head !=null){
					if(head.equals("Content-Length")){	//根据头部信息获取文件长度
						fileLength=Integer.parseInt(httpConnection.getHeaderField(head));
						break;
					}
				}else{
					break;
				}
			}
			
			
		} catch (Exception e) {
			System.out.println("获取文件长度出错:"+e.getMessage());
		}
		Log.log("文件长度"+fileLength);
		return fileLength;
		
	}
	
	private void errorCode(int errorCode){
		System.out.println("错误代码:"+errorCode);
	}
	
	//读取文件指针位置
	private void readInfo(){
		try {
			//创建数据输出流
			DataInputStream input=new DataInputStream(new FileInputStream(fileName));
			int  count=input.readInt();						//读取分成的线程下载个数
			startPosition=new long[count];					//设置开始线程
			endPosition=new long[count];					//设置结束线程
			for (int i = 0; i < startPosition.length; i++) {
				startPosition[i]=input.readLong();			//读取每个线程的开始位置
				endPosition[i]=input.readLong();			//读取每个线程的结束位置
			}
			input.close();
		} catch (Exception e) {
			System.out.println("读取文件指针位置出错:"+e.getMessage());
		}
	}
	
	
	
}
//扩展线程类,实现部分文件的抓取
class FileFetch extends Thread{
	String webAddr;						//网址
	long startPosition; 				//开始位置
	long endPosition;					//结束位置
	int threadID;						//线程编号
	boolean downLoadOver=false;			//下载结束
	boolean isStopGet=false;			//是否停止请求
	FileAccess fileAccessI=null;			//存储文件的类
	
	public FileFetch(String surl,String sname, long startPosition, long endPosition,
			int threadID) throws IOException {
		this.webAddr = surl;
		this.startPosition = startPosition;
		this.endPosition = endPosition;
		this.threadID = threadID;
		this.fileAccessI = new FileAccess(sname,startPosition);
	}
	
	//实现线程的方法
	public void run(){
		while (startPosition < endPosition && !isStopGet) {
			try {
				URL url=new URL(webAddr);		//根据网络资源创建URL对象
				HttpURLConnection httpConnection=(HttpURLConnection)url.openConnection(); //创建 打开的连接对象
				//设置描述发出的HTTP请求的终端的信息
				httpConnection.setRequestProperty("User-Agent", "NetFox");	
				String sproperty="byte="+startPosition+"-";
				httpConnection.setRequestProperty("RANGE", sproperty);
				Log.log(sproperty);
				
				//获取 网络资源的输入流
				InputStream input=httpConnection.getInputStream();
				
				byte[] b=new byte[1024];
				int nRead;
				//循环将文件下载制定目录
				while ((nRead=input.read(b, 0, 1024))>0 && startPosition < endPosition && !isStopGet) {
					startPosition+=fileAccessI.write(b, 0, nRead); //调用方法将内容写入文件
				}
				Log.log("线程\t"+(threadID+1)+"\t结束....");
				downLoadOver=true;
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
	
	//打印回应的头的信息
	public void logResponseHead(HttpURLConnection con){
		for (int i = 1; ; i++) {
			String header=con.getHeaderFieldKey(i);	//循环答应回应的头信息
			if(header!=null){
				Log.log(header+":"+con.getHeaderField(header));
			}else
				break;
		}
	}
	
	public void splitterStop(){
		isStopGet=true;
	}
	
}

//存储文件的类
class FileAccess implements Serializable{
	RandomAccessFile saveFile; 			//要保存的文件
	long position;
	public FileAccess() throws IOException {
		this("",0);
	}
	public FileAccess(String sname, long position) throws IOException {
		this.saveFile =new RandomAccessFile(sname, "rw");	//创建随机读取对象, 以 读/写的方式
		this.position = position;
		saveFile.seek(position);							//设置指针位置
	}	
	//将字符数据 写入文件
	public synchronized int write(byte[] b,int start,int length){
		int n=-1;
		try {
			saveFile.write(b,start,length);
			n=length;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return n;
	}
}

//传输保存信息的类
class TranBean {
	private String webAddr;				//下载地址
	private String fileDir;				//下载到指定的目录
	private String fileName;			//下载后文件的新名字
	private int count;					//文件分几个线程下载, 默认为  3个 
	public TranBean() {					//默认的构造方法
		this("","","",3);
	}									//带参数的构造方法
	public TranBean(String webAddr, String fileDir, String fileName, int count) {
		this.webAddr = webAddr;
		this.fileDir = fileDir;
		this.fileName = fileName;
		this.count = count;
	}
	public String getWebAddr() {
		return webAddr;
	}
	public void setWebAddr(String webAddr) {
		this.webAddr = webAddr;
	}
	public String getFileDir() {
		return fileDir;
	}
	public void setFileDir(String fileDir) {
		this.fileDir = fileDir;
	}
	public String getFileName() {
		return fileName;
	}
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	
}
//线程运行信息显示的日志类
class Log{
	public Log(){}
	public static void sleep(int nsecond){
		try {
			Thread.sleep(nsecond);
		} catch (Exception e) {
			System.out.println("线程沉睡");
		}
	}
	public static void log(String message){  		//显示日志信息
		System.err.println(message);
		
	}
	public static void log(int message){  			//显示日志信息
		System.err.println(message);
		
	}
}

0
2
分享到:
评论
3 楼 zy116494718 2015-08-10  
Luob. 写道
yao752915708 写道

 //将字符数据 写入文件  
     public synchronized int write(byte[] b,int start,int length){  
        int n=-1;  
         try {  
             saveFile.write(b,start,length);  
             n=length;  
         } catch (Exception e) {  
             e.printStackTrace();  
         }  
        return n;  
     }  
}   



synchronized 这个是不是没有必要呢? 线程读取的都是不同的位置! 菜鸟一枚,勿喷!

我刚试试了下 ,不行 ,下载下来的 文件 破坏了,但是 大小是对的.
说明  多线程下载 是指 将一个文件分多个线程先下载, 但是写到同一个文件中的时候 必须按顺序写. 如果不是写到同一个文件 ,这里 就不需要 synchronized ,你看这样对吗?


我多线程下载,那么有可能后面的线程先完成流的下载,唯独第一块最慢,这时我下载中断,也就等于我所有的块都没传,因为写入文件必须按顺序,还得从第一块重新传。对吧,楼主?
2 楼 Luob. 2013-03-14  
yao752915708 写道

 //将字符数据 写入文件  
     public synchronized int write(byte[] b,int start,int length){  
        int n=-1;  
         try {  
             saveFile.write(b,start,length);  
             n=length;  
         } catch (Exception e) {  
             e.printStackTrace();  
         }  
        return n;  
     }  
}   



synchronized 这个是不是没有必要呢? 线程读取的都是不同的位置! 菜鸟一枚,勿喷!

我刚试试了下 ,不行 ,下载下来的 文件 破坏了,但是 大小是对的.
说明  多线程下载 是指 将一个文件分多个线程先下载, 但是写到同一个文件中的时候 必须按顺序写. 如果不是写到同一个文件 ,这里 就不需要 synchronized ,你看这样对吗?
1 楼 yao752915708 2013-03-14  

 //将字符数据 写入文件  
     public synchronized int write(byte[] b,int start,int length){  
        int n=-1;  
         try {  
             saveFile.write(b,start,length);  
             n=length;  
         } catch (Exception e) {  
             e.printStackTrace();  
         }  
        return n;  
     }  
}   



synchronized 这个是不是没有必要呢? 线程读取的都是不同的位置! 菜鸟一枚,勿喷!

相关推荐

Global site tag (gtag.js) - Google Analytics