`
ilikeido
  • 浏览: 27424 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

重游java系列一 线程二(多线程下载和断点续传)

阅读更多
   本篇文章以你文件下载中的多线程下载以及断点续传为问题出发点,主要回顾一下多线程在实际开发中的应用和具体实现。

    多线程下载的关键点在于对一个下载任务进行切分,即计算每个任务线程对应的实际文件中的起始点和终止点。在每个线程中采用数据流方式对远程文件进行连接,这里有个知识点,即http头Rander参数,详见http://guoba6688-sina-com.iteye.com/blog/786036,通过该参数可以实现读取远程文件的指定部分。

    下面的实例的应用环境为android,具体看代码:

   
package com.fsti.android.foyer.net;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;

import android.util.Log;

/**
 * 文件下载线程
 * 
 * @author ilikeido
 * @modifyAuthor 
 * 
 * @creatTime 2011-4-7 下午02:59:04
 */
public class FileDownloadThread extends Thread {
	
	private static final int BUFFER_SIZE = 1024;
	private URL url;//地址
	private File file;//保存文件
	private int startPosition;//开始位置
	private int endPosition;//结束位置
	private int curPosition;//当前位置
	
	private ThreadCutter cutter;
	
	// 用于标识当前线程是否下载完成
	private boolean finished = false;
	private int downloadSize = 0;//下载的文件大小

	public FileDownloadThread(URL url, File file, int startPosition,
			int endPosition,ThreadCutter cutter) {
		this.url = url;
		this.file = file;
		this.startPosition = startPosition;
		this.curPosition = startPosition;
		this.endPosition = endPosition;
		this.cutter = cutter;
	}

	@Override
	public void run() {
		BufferedInputStream bis = null;
		RandomAccessFile fos = null;
		byte[] buf = new byte[BUFFER_SIZE];
		URLConnection con = null;
		try {
			fos = new RandomAccessFile(file, "rw");
			downloadSize = (int) fos.length();
			con = url.openConnection();
			con.setAllowUserInteraction(true);
			// 设置当前线程下载的起点,终点
			con.setRequestProperty("Range", "bytes=" + (startPosition+downloadSize) + "-"
					+ endPosition);
			// 使用java中的RandomAccessFile对文件进行随机读写操作
			
			curPosition = startPosition + downloadSize;
			// 设置开始写文件的位置
			fos.seek(downloadSize);
			bis = new BufferedInputStream(con.getInputStream());
			// 开始循环以流的形式读写文件
			while (cutter.getStatu() == 1 && curPosition < endPosition) {
				int len = bis.read(buf, 0, BUFFER_SIZE);
				if (len == -1) {
					break;
				}
				fos.write(buf, 0, len);
				curPosition = curPosition + len;
				if (curPosition > endPosition) {
					downloadSize += len - (curPosition - endPosition) + 1;
				} else {
					downloadSize += len;
				}
			}
			// 下载完成设为true
			this.finished = true;
			bis.close();
			fos.close();
		} catch (IOException e) {
			Log.d(getName() + "Error:", e.getMessage());
		}
	}

	public boolean isFinished() {
		return finished;
	}

	public int getDownloadSize() {
		return downloadSize;
	}
	
	public File getFile(){
		return file;
	}
}

以上为线程下载任务,主要负责下载任务,下面看一下管理器
package com.fsti.android.foyer.net;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import android.util.Log;

/**
 * 文件分割器(用于多线程下载)
 * 
 * @author ilikeido
 * @modifyAuthor
 * 
 * @creatTime 2011-4-7 下午03:05:53
 */
public class DownThreadManager {

	private static final String TAG = "ThreadCutter";

	private int threadSize;// 线程数

	private URL url;// 地址

	private String fileName;

	private int totalProgress;// 整体进度

	private int filelength;// 文件大小

	File dir;
	File saveFile;

	private int statu;// 状态 0:停止 1:正常 2:暂停 3:完成 -1:错误

	private List<FileDownloadThread> threads;

	public DownThreadManager(int threadSize, String urlStr, File dir)
			throws IOException {
		this.dir = dir;
		this.threadSize = threadSize;
		this.url = new URL(urlStr);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		filelength = conn.getContentLength();
		if(filelength>0){
			initFileName(conn,urlStr);
			cutThreads();
		}else{
			throw new RuntimeException("file not find");
		}
		
	}

	/**
	 * 分割下载任务
	 */
	private void cutThreads() {
		threads = new ArrayList<FileDownloadThread>();
		int cutsize = filelength / threadSize;
		for (int i = 0; i < threadSize; i++) {
			File tempFile = new File(dir, fileName + i + ".temp");
			int startPosition = cutsize * i;
			int endPosition = 0;
			FileDownloadThread thread = null;
			if (i < threadSize - 1) {
				endPosition = cutsize * i + cutsize - 1;
				thread = new FileDownloadThread(url, tempFile, startPosition,
						endPosition, this);
			} else {
				thread = new FileDownloadThread(url, tempFile, startPosition,
						filelength, this);
			}
			threads.add(thread);
		}
	}

	/**
	 * 开始下载任务
	 */
	public void start() {
		Iterator<FileDownloadThread> itertor = threads.iterator();
		statu = 1;
		while (itertor.hasNext())
			itertor.next().start();
	}

	/**
	 * 获取当前下载的文件大小
	 * 
	 * @return
	 */
	public int getDownloadSize() {
		Iterator<FileDownloadThread> itertor = threads.iterator();
		int downloadSize = 0;
		while (itertor.hasNext()) {
			downloadSize += itertor.next().getDownloadSize();
		}
		if (downloadSize > 0) {
			this.totalProgress = (int) (((float) downloadSize / filelength) * 100);
		}
		if (downloadSize == filelength) {
			this.statu = 3;
		}
		return downloadSize;
	}

	public void merge() throws IOException {
		RandomAccessFile ok = new RandomAccessFile(saveFile,"rw");
		Iterator<FileDownloadThread> itertor = threads.iterator();
		while (itertor.hasNext()) {
			File file = itertor.next().getFile();
			RandomAccessFile read = new RandomAccessFile(file, "r");
			byte[] b = new byte[1024];
			int n = 0;
			while ((n = read.read(b)) != -1) {
				ok.write(b, 0, n);
			}
			read.close();
			file.delete();
		}
		ok.close();
	}

	public void stop() {
		this.statu = 2;
	}

	public int getStatu() {
		return statu;
	}

	public void setStatu(int statu) {
		this.statu = statu;
	}

	public int getTotalProgress() {
		return totalProgress;
	}

	/**
	 * 获取文件名
	 * 
	 * @param http
	 */
	public void initFileName(HttpURLConnection http,String urlStr) {
		String filename = urlStr.substring(urlStr.lastIndexOf('/') + 1);
		if(filename.indexOf(".")>=0){
			fileName = filename;
		}else{
			Map<String, String> header = getHttpResponseHeader(http);
			for (Map.Entry<String, String> entry : header.entrySet()) {
				String key = entry.getKey() != null ? entry.getKey() + ":" : "";
				print(key + entry.getValue());
			}
			String dispostion  = header.get("content-disposition");
			int index = 0;
			if((index = dispostion.indexOf("filename")) >-1){
				String filenametemp = dispostion.substring(index,dispostion.length());
				String[] temp = filenametemp.split("\"");
				fileName = temp[1];
			}
		}
		saveFile = new File(dir, fileName);
	}

	/**
	 * 获取Http响应头字段
	 * 
	 * @param http
	 * @return
	 */
	public static Map<String, String> getHttpResponseHeader(
			HttpURLConnection http) {
		Map<String, String> header = new LinkedHashMap<String, String>();
		for (int i = 0;; i++) {
			String mine = http.getHeaderField(i);
			if (mine == null)
				break;
			header.put(http.getHeaderFieldKey(i), mine);
		}
		return header;
	}

	private static void print(String msg) {
		Log.i(TAG, msg);
	}
	
	public File getSaveFile(){
		return this.getSaveFile();
	}

}

该管理器主要的任务是分割下载任务,对线程的管理(通过statu参数)、以及各线程下载完成后文件的合并等。
下面是实际中的调用:
package com.fsti.android.foyer;

import java.io.File;
import java.io.IOException;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.fsti.android.foyer.net.ThreadCutter;

public class MainActivity extends Activity implements OnClickListener,DialogInterface.OnClickListener{
    /** Called when the activity is first created. */
	
	ProgressDialog dialog = null;
	int threadNum = 2;
	String urlStr= "http://www.piaoao.com/resources/updatefiles/alldown/WingLetter_GPiaoao.apk";//"http://nutla.googlecode.com/files/Nuta9.pdf";//"http://dl_dir.qq.com/qqfile/qq/Android/Tencent_Wblog%20V2.0.1.apk";//

	ThreadCutter cutter = null;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(this);
    }

	@Override
	public void onClick(View arg0) {
		dialog = new ProgressDialog(this);
		dialog.setProgress(59);
		dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		dialog.setTitle("下载中");
		dialog.setMessage("正在下载应用程序");
		dialog.setButton("取消", this);
		dialog.show();
		download();
	}

	@Override
	public void onClick(DialogInterface arg0, int arg1) {
		cutter.stop();
		dialog.dismiss();
	}
	
	public void download(){
		final File dir = Environment.getExternalStorageDirectory();
		int cutterNum = 3;
		try {
			cutter = new ThreadCutter(cutterNum, urlStr, dir);
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					cutter.start();
					while(cutter.getTotalProgress() < 100){
						cutter.getDownloadSize();
						try {
							Thread.sleep(900);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						dialog.setProgress(cutter.getTotalProgress());
					}
					try {
						cutter.merge();
						cutter.setStatu(3);
					} catch (IOException e) {
						e.printStackTrace();
					}
					dialog.dismiss();
					try {
						if(cutter.getSaveFile().getAbsolutePath().endsWith(".apk")){
							Intent localIntent1 = new Intent("android.intent.action.VIEW");
						    Uri localUri = Uri.fromFile(cutter.getSaveFile());
						    localIntent1.setDataAndType(localUri, "application/vnd.android.package-archive");
						    startActivity(localIntent1);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			};
			new Thread(runnable).start();
		} catch (IOException e) {
			cutter.setStatu(4);
		}
	}
	
}



分享到:
评论

相关推荐

    多线程下载支持断点续传

    在IT领域,多线程下载和断点续传是提高下载效率和用户体验的重要技术。这里,我们将深入探讨这两个概念,并结合使用HttpURLConnection实现的多线程下载工具进行讲解。 首先,多线程下载是一种利用网络资源的方式,...

    java断点续传,刚刚整理出来多线程处理

    然而,实现多线程断点续传需要解决几个问题: 1. **同步管理**:多个线程可能会同时访问同一个文件的部分,因此需要使用`synchronized`关键字或`Lock`对象来确保并发安全。 2. **断点信息共享**:每个线程需要知道...

    Java实现多线程下载和断点续传

    1. 把每个下载文件切成若干个块(Block),然后得到一个位图,用来标记每个块的下载情况,并保存到文件里,用于实现断点续传。 2. HTTP Header里增加Range,如果服务器返回Cotent-Range 说明服务器支持文件定位,可以...

    java ftp 多线程 断点续传等知识

    而"多线程"和"断点"这两个文件名可能是指相关示例代码或文档,可以进一步帮助你理解和实践Java FTP的多线程下载和断点续传。 在实际应用中,还需要考虑其他因素,如错误处理、网络状况的监控、文件完整性检查等。...

    java多线程断点续传下载

    Java多线程断点续传下载是一个复杂但实用的技术,尤其在处理大文件或网络不稳定时,能够提高用户体验并优化资源利用。以下是对这个主题的详细解析: **1. Java多线程** Java多线程是指在一个Java应用程序中同时执行...

    java 多线程下载和断点续传

    ### Java多线程下载与断点续传技术详解 #### 一、背景介绍 随着互联网技术的发展,数据传输成为日常开发中的重要环节之一。在实际应用中,常常需要下载大文件,例如视频、大型应用程序等。传统的单线程下载方式在...

    java实现多线程断点续传下载

    通过以上步骤,我们可以构建一个功能完备的Java多线程断点续传下载程序。这个项目不仅可以帮助初学者理解多线程和网络编程的基本概念,也可以作为实际项目开发中的一个参考模板。对于想要深入研究Java网络编程和并发...

    java实现FTP多线程断点续传

    ### Java实现FTP多线程断点续传:深入解析与技术要点 在现代软件开发中,数据传输是一项基本且关键的任务,特别是在处理大文件时,断点续传功能显得尤为重要。断点续传允许在网络连接中断后恢复传输,避免了重新...

    Java 多线程下载及断点续传示例

    在Java编程中,多线程下载和断点续传是两个关键的概念,它们在处理大文件下载时尤其重要。这两个技术结合使用可以显著提高下载效率并优化用户体验。 **多线程下载** 多线程下载是一种将文件分割成多个部分,然后...

    多线程 断点续传 下载

    在Java中实现一个多线程断点续传的下载器,一般步骤如下: 1. 分割文件:根据文件大小和期望的线程数,计算每个线程需要下载的数据块大小。 2. 创建线程池:使用`ExecutorService`,如`Executors....

    socket做的支持多线程断点上传or断点续传Java源码

    标题中的“socket做的支持多线程断点上传or断点续传Java源码”涉及到的是在网络编程中,如何使用Java的Socket API实现一个能够处理断点上传和断点续传功能的服务。这是一个高级的网络编程任务,通常在大型文件传输...

    自己收集的多个Java FTP断点续传的例子源码

    java实现FTP多线程断点续传,上传下载! - java学习与交流 - j2ee学习网 - j2ee学习网 (2012年5月21日) 用 Java 实现断点续传 (HTTP) (2012年5月21日) Java写的支持断点续传的FTP--crybaby的博客 (2012年5月21日) ...

    java多线程断点续传[借鉴].pdf

    总结来说,Java实现的多线程断点续传涉及的技术点包括: 1. 并发编程:使用`ExecutorService`、`CountDownLatch`进行线程管理和同步。 2. 文件操作:分析和合并临时文件,实现断点续传。 3. 网络I/O:通过`...

    java 多线程断点续传

    总结来说,Java中的多线程断点续传结合了并发处理和状态管理,通过分割文件并行处理,提高了文件传输效率,同时通过记录和恢复断点,实现了在网络不稳定情况下的可靠性。这是一项对大型文件上传和下载非常重要的技术...

    JAVA多线程断点续传下载程序

    Java多线程断点续传下载程序是一种高级的软件实现技术,它结合了Java的并发处理能力和文件传输的策略,以提高下载效率和用户体验。在这个项目中,我们主要关注两个核心概念:多线程和断点续传。 首先,多线程是Java...

    一个支持多线程断点续传功能的Android下载工具.zip

    在Android平台上,开发一款支持多线程断点续传功能的下载工具是一项技术挑战,它涉及到网络编程、文件处理以及线程管理等多个方面。这款名为"DownloadHelper"的项目,显然是一个致力于解决这些问题的开源解决方案。 ...

    java断点续传与多线程下载

    Java 断点续传与多线程下载是网络编程中两个重要的技术,它们在处理大文件下载时尤其有用。断点续传允许用户在下载中断后从上次停止的地方继续,而多线程下载则通过同时从服务器获取多个数据块来提高下载速度。接...

    java FTP多线程 批量 断点续传

    Java FTP多线程批量断点续传是一种在Java编程中实现高效、稳定文件传输的方法,尤其适用于大文件的上传和下载。在这个过程中,我们利用FTP(File Transfer Protocol)协议,结合多线程技术和断点续传功能,可以显著...

    Android 多线程断点续传下载

    在Android开发中,多线程断点续传下载是一项重要的技术,它允许用户在中断下载后,从上次停止的地方继续下载,提高了用户体验。这个技术主要涉及到网络编程、多线程处理以及文件操作等多个方面。接下来,我们将深入...

    Java多线程与线程安全实践-基于Http协议的断点续传

    Java多线程与线程安全实践-基于Http协议的断点续传 Java多线程与线程安全实践-基于Http协议的断点续传 Java多线程与线程安全实践-基于Http协议的断点续传 Java多线程与线程安全实践-基于Http协议的断点续传 Java多...

Global site tag (gtag.js) - Google Analytics