Java--多线程断点下载

Main.java

import java.net.HttpURLConnection;
import java.net.URL;


public class Main {
	
	private static Messages messages = new Messages();
	
	private static CallBack callback = new CallBack() {
		@Override
		public void schedule(Messages messages) {
			long totalDownloadSize = messages.getTotalDownloadSize();
			int currentDownloadSize = messages.getCurrentDownloadSize();
			System.out.println((float)currentDownloadSize * 100.0f / (float)totalDownloadSize + "%");
		}
		
		@Override
		public void handlerMessages(Messages messages) {
			
		}
		
		@Override
		public void fail(Messages messages) {
			System.out.println(messages.getMsg());
		}
	};
	
	public static void main(String[] args) throws Exception {
		long startTime = System.currentTimeMillis();
		String downloadUrl = "http://www.xiazaiba.com/route.php?ct=stat&ac=stat_ads&id=CG8BOVNg&g=aHR0cDovL3d3dy51aWJpYS5jb20vamttc2V0dXAuZXhl";
		URL url = new URL(downloadUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("GET");
		conn.setReadTimeout(1000 * 5);
		conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
		conn.setRequestProperty("Accept-Language", "zh-CN");
		conn.setRequestProperty("Referer", downloadUrl); 
		conn.setRequestProperty("Charset", "UTF-8");
		conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
		conn.setRequestProperty("Connection", "Keep-Alive");
		conn.connect();
		
		if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
			FileDownloader loader = new FileDownloader(callback, 4, messages);
			int size = conn.getContentLength();//根据响应获取文件大小
			loader.setSavePath("E:\haoya.exe");
			loader.setConfigPath("E:\haoya.properties");
			loader.download(downloadUrl, size);
		}else{
			System.out.println(conn.getResponseCode());
		}
		
		conn.disconnect();
		long endTime = System.currentTimeMillis();
		System.out.println("Time" + (endTime - startTime));
	}
}


FileDownloader.java

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.util.Map;

public class FileDownloader implements DownLoadCallBack {
	//单个线程已经下载的长度
	private int downloadSize;
	//线程数 
	private DownloadThread[] threads;
	// 每条线程下载的长度 
	private int block;
	//文件保存路径
	private String savePath;
	//配置文件路径
	private String configPath;
	private File saveFile;
	private boolean isNext;
	private int call_curr;
	private int call_total;
	private PropertiesHelper propertiesHelper;    
	private Map<String, String> datas;
	private Messages messages;
	private CallBack callback;
	//总的下载长度
	private int downloadLength;
	//是否退出下载
	private boolean exit;

	public FileDownloader(CallBack callback, int threadNum, Messages messages) {
		this.callback = callback;
		this.threads = new DownloadThread[threadNum];
		this.isNext = false;
		this.call_total = threadNum;
		this.downloadSize = 0;
		this.downloadLength = 0;
		this.messages = messages;
		this.exit = false;
	}

	/**
	 * 开始下载文件
	 */
	public void download(String downloadUrl, long size){
		try {
			messages.setTotalDownloadSize(size);
			saveFile = new File(savePath);
			RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rw");
			accessFile.setLength(size); 
			accessFile.close();
			
			block = (int) ((size % this.threads.length) == 0 ? size
					/ this.threads.length : size / this.threads.length + 1);
			URL url = new URL(downloadUrl);

			propertiesHelper = new PropertiesHelper(configPath); 
			//如果配置文件不存在,则创建
			if(!FileOperate.isFileExist(configPath)){
				FileOperate.createFile(configPath, false);
				initProperties();
			}else{      
				datas = propertiesHelper.getAllProperties();
				if(datas.size() == 0) initProperties();
			}   

			for(int i = 0; i < threads.length; i++){
				String value = datas.get(Constant.THREADID + (i + 1));
				if(value == null) value = "0";
				downloadSize = Integer.parseInt(value);  
				downloadLength += downloadSize;
			}

			for (int i = 0; i < threads.length; i++) {// 开启线程进行下载 
				String value = datas.get(String.valueOf(Constant.THREADID + (i + 1)));
				if(value == null) value = "0";
				downloadSize = Integer.parseInt(value);  
				if(downloadSize == block){  
					call_curr++;
					if(call_curr == call_total){
						isNext = true;
						break;
					}
					continue;
				}  
				threads[i] = new DownloadThread(url, saveFile, block,  
						downloadSize, i + 1, messages, callback, this);
				threads[i].setPriority(7);
				threads[i].start();
			}
			 
			while(!isNext());
		} catch (IOException e) { 
			messages.setMsg("FileDownloader:download:" + e.getMessage());
			callback.fail(messages);  
		}
	}   

	private synchronized boolean isNext() {
		return isNext;
	}
	
	private void initProperties() {
		for(int i = 0; i < threads.length; i++){
			propertiesHelper.setProperties(Constant.THREADID + (i + 1), "0"); 
		}
		datas = propertiesHelper.getAllProperties();
	}

	@Override
	public synchronized void isFinished() {
		call_curr++;
		if (call_curr == call_total){
			isNext = true;
		}
	}

	public synchronized void update(String key, String value){
		propertiesHelper.setProperties(key, value);
	}

	@Override  
	public synchronized int getDownloadLength() {
		return downloadLength;
	}

	@Override
	public synchronized void append(int size) {
		downloadLength += size;
	}

	public String getSavePath() {
		return savePath;
	}

	public void setSavePath(String savePath) {
		this.savePath = savePath;
	}

	public String getConfigPath() {
		return configPath;
	}

	public void setConfigPath(String configPath) {
		this.configPath = configPath;
	}

	@Override
	public boolean getExit() {
		return exit;
	}

	@Override
	public void setExit(boolean isExit) {
		exit = isExit;
	}
}


DownloadThread.java

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownloadThread extends Thread {
	private File saveFile;
	private URL downUrl;
	private int block;
	private int threadId = -1;    
	private CallBack callback;
	private DownLoadCallBack downLoadCallBack;
	private int downloadSize;
	private Messages messages; 

	public DownloadThread(URL downUrl, File saveFile, int block, int downloadSize, int threadId, Messages messages, CallBack callback, DownLoadCallBack downLoadCallBack) {
		this.downLoadCallBack = downLoadCallBack;
		this.downUrl = downUrl;
		this.saveFile = saveFile;
		this.block = block;
		this.threadId = threadId;
		this.callback = callback;
		this.downloadSize = downloadSize;
		this.messages = messages;
	}

	@Override
	public void run() {
		HttpURLConnection conn = null;
		InputStream inStream = null;
		RandomAccessFile threadfile = null;   
		try {  
			conn = (HttpURLConnection) downUrl.openConnection();
			conn.setRequestMethod(Constant.GET);
			conn.setReadTimeout(1000 * 8);
			int startPos = block * (threadId - 1) + downloadSize;//开始位置   
			int endPos = block * threadId -1;//结束位置  
			conn.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围

			if(conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
				inStream = conn.getInputStream();   
				byte[] buffer = new byte[Constant.BUFFER * 10];
				int offset = 0;
				threadfile = new RandomAccessFile(saveFile, "rwd");  
				threadfile.seek(startPos);
				String key = Constant.THREADID + String.valueOf(threadId);  
				//messages,文件总大小,下载的当前大小
				while (!downLoadCallBack.getExit() && (offset = inStream.read(buffer)) != -1) {   
					threadfile.write(buffer, 0, offset);   
					downLoadCallBack.append(offset);      
					downloadSize += offset;
					downLoadCallBack.update(key, String.valueOf(downloadSize));
					int size = downLoadCallBack.getDownloadLength();
					messages.setCurrentDownloadSize(size);
					callback.schedule(messages);   
				}
			}
		} catch (IOException e) {
			downLoadCallBack.setExit(true);
			messages.setMsg("第" + threadId + "条线程下载失败"); 
			callback.fail(messages);   
		}finally{ 
			try {
				if(threadfile != null){
					threadfile.close();  
				}
				if(inStream != null){  
					inStream.close();
				} 
				if(conn != null){
					conn.disconnect();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			downLoadCallBack.isFinished();
		}
	}
}



DownLoadCallBack.java

public interface DownLoadCallBack {
	public void isFinished();
	
	public void update(String key, String value);
	
	public void append(int size);
	
	public int getDownloadLength();
	
	public boolean getExit();
	
	public void setExit(boolean isExit);
}


CallBack.java

public interface CallBack {
	
	public void handlerMessages(Messages messages);
	
	public void schedule(Messages messages);
	
	public void fail(Messages messages);
}


FileOperate.java

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class FileOperate {
	/**
	 * 在指定路径下创建新文件
	 * @param filePath      文件路径
	 * @return
	 * @throws IOException
	 */
	public static File createFile(String filePath, boolean isDeleteAllFiles) {
		String parentPath = filePath.substring(0, filePath.lastIndexOf(File.separator));
		createFolders(parentPath, isDeleteAllFiles);
		File file = new File(filePath);
		try {
			if(!file.createNewFile()){
				file.delete();
				file.createNewFile();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return file;
	}

	/**
	 * 判断配置文件是否存在
	 * @param filePath  文件路径
	 * @return
	 */
	public static boolean isFileExist(String filePath){
		File file = new File(filePath);
		return file.exists();
	}

	/**
	 * 取得指定目录下的所有文件夹名
	 * @return
	 */
	public static List<String> getFilesName(String filePath){
		List<String> files_name = null;
		File file = new File(filePath);
		if(file.exists()){
			files_name = new ArrayList<String>();
			File[] files = file.listFiles();
			for (File file2 : files) {
				if(file2.isDirectory()){
					files_name.add(file2.getName());
				}
			}
		}
		return files_name;
	}

	public static void deleteFiles(File file){
		if(file.exists()){
			File[] files = file.listFiles();
			for(File f : files){
				if(f.isFile()) f.delete();
			}
		}
	}

	public static File createFolders(String path, boolean isDeleteAllFiles){
		File file = new File(path);
		if(file.exists() && file.isDirectory()){
			if(isDeleteAllFiles) deleteFiles(file);
		}else{
			file.mkdirs();
		}
		return file;
	}

	public static boolean deleteFile(String filePath){
		boolean isDeleted = false;
		File file = new File(filePath);
		if(file.exists()){
			if(file.isFile()) {
				file.delete();
				isDeleted = true;
			}
		}
		return isDeleted;
	}

	public static void deleteMkdir(String filePath){
		File file = new File(filePath);
		if(file.exists() && file.isDirectory()){
			file.delete();
		}
	}


	/*********删除文件夹******/
	public static boolean deleteFolders(String filePath){
		boolean isDeleteSuccess = false;
		LinkedList<String> folderList = new LinkedList<String>();
		folderList.add(filePath);

		while(folderList.size() > 0){
			File file1 = new File(folderList.poll());
			File[] files1 = file1.listFiles();
			ArrayList<File> fileList = new ArrayList<File>();
			for(int i = 0; i < fileList.size(); i++){
				if(files1[i].isDirectory()){
					folderList.add(files1[i].getPath());
				}else{
					fileList.add(files1[i]);
				}
			}
			//删除文件
			for(File file : fileList){
				file.delete();
			}
		}

		//删除文件夹
		folderList = new LinkedList<String>();
		folderList.add(filePath);
		while(folderList.size() > 0){
			File file2 = new File(folderList.getLast());
			if(file2.delete()){
				folderList.removeLast();
			}else{
				File[] files2 = file2.listFiles();
				for(int i = 0; i < files2.length; i++){
					folderList.add(files2[i].getPath());
				}
			}
		}
		if(folderList.size() == 0) isDeleteSuccess = true;
		return isDeleteSuccess;
	}

	public static boolean moveFile(String srcFilePath, String dstFilePath){
		boolean isMoveFileSuccess = false;
		
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			if(isFileExist(dstFilePath)) deleteFile(dstFilePath);
			File dstFile = createFile(dstFilePath, false);
			fis = new FileInputStream(new File(srcFilePath));
			fos = new FileOutputStream(dstFile);
			bis = new BufferedInputStream(fis);
			bos = new BufferedOutputStream(fos, Constant.BUFFER * 10);
			int count = -1;
			byte[] buffer = new byte[Constant.BUFFER * 10];
			while((count = bis.read(buffer, 0, Constant.BUFFER * 10)) != -1){
				bos.write(buffer, 0, count);
				bos.flush();
			}
			isMoveFileSuccess = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				if(bos != null){
					bos.close();
				}
				if(fos != null){
					fos.close();
				}
				if(bis != null){
					bis.close();
				}
				if(fis != null){
					fis.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return isMoveFileSuccess;
	}
}



Constant.java

public class Constant {
	public static final int BUFFER = 1024;
	public static final String UTF = "utf-8";
	public static final String THREADID = "threadId";  
	
	public static final String OGG = ".ogg";
	public static final String PACK = ".pack";
	public static final String PNG = ".png";
	public static final String MP3 = ".mp3";
	public static final String PROPERTIES = ".properties";
	public static final String TMX = ".tmx";
	public static final String GET = "GET";
}


Messages.java

public class Messages {
	private String msg;
	private long totalDownloadSize;
	private int currentDownloadSize;
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public long getTotalDownloadSize() {
		return totalDownloadSize;
	}
	public void setTotalDownloadSize(long totalDownloadSize) {
		this.totalDownloadSize = totalDownloadSize;
	}
	public int getCurrentDownloadSize() {
		return currentDownloadSize;
	}
	public void setCurrentDownloadSize(int currentDownloadSize) {
		this.currentDownloadSize = currentDownloadSize;
	}
}


PropertiesHelper.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class PropertiesHelper {

	private String project_root = "";

	public PropertiesHelper(String filePath) {
		if (filePath != null && filePath.length() > 0) {
			project_root = project_root + filePath;
		}
	}

	public String getProperties(String key) {
		InputStream fis = null;
		try {
			Properties prop = new Properties();
			fis = new FileInputStream(project_root);
			prop.load(fis);
			return prop.getProperty(key);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	public Map<String, String> getAllProperties() {
		InputStream fis = null;
		Map<String, String> params = new HashMap<String, String>();
		try {
			Properties prop = new Properties();
			fis = new FileInputStream(project_root);
			prop.load(fis);
			Enumeration en = prop.propertyNames();
			while (en.hasMoreElements()) {
				String key = (String) en.nextElement();
				String value = prop.getProperty(key);
				params.put(key, value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return params;
	}

	public void setProperties(String key, String value) {
		Properties prop = new Properties();
		FileOutputStream outputFile = null;
		InputStream fis = null;
		try {
			// 输入流和输出流要分开处理, 放一起会造成写入时覆盖以前的属性
			fis = new FileInputStream(project_root);
			// 先载入已经有的属性文件
			prop.load(fis);

			// 追加新的属性
			prop.setProperty(key, value);

			// 写入属性
			outputFile = new FileOutputStream(project_root);
			prop.store(outputFile, "");

			outputFile.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null)
					fis.close();
				if (outputFile != null)
					outputFile.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}



原文地址:https://www.cnblogs.com/keanuyaoo/p/3258062.html