Java多线程文件下载

一. 多线程下载文件考虑处理步骤:

1. 如何获取文件的长度

2. 合理的创建线程数量,并计算每一个线程下载的长度

3. 如何将多个线程下载的字节写入到文件中

二. 代码实现如下:

package com.bochao.download;

import java.io.File;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * HTTP多线程下载
 * 
 * @Author DuanCZ
 * @Date 2015年9月30日-上午9:28:28
 */

public class HttpMulitThreadDownload {

	// 下载文件路径
	private String downloadFilePath = null;
	// 保存文件路径
	private String saveFileDir = "c:\\";
	// 默认合理并发线程数
	private int threadCount = Runtime.getRuntime().availableProcessors() * 2;
	// 新文件名称
	private String newFileName = null;

	public HttpMulitThreadDownload(int threadCount, String downloadFilePath, String saveFileDir, String newFileName) {
		// 用户指定线程数如果小于默认线程数则使用用户指定线程数
		if (threadCount < this.threadCount) {
			this.threadCount = threadCount;
		}
		this.downloadFilePath = downloadFilePath;
		this.saveFileDir = saveFileDir;
		this.newFileName = newFileName;
	}

	public void MulitThreadDownload() {

		// 数据合法性验证
		if (null == downloadFilePath || downloadFilePath.isEmpty()) {
			throw new RuntimeException("请指定下载路径!");
		}
		if (null == saveFileDir || saveFileDir.isEmpty()) {
			throw new RuntimeException("请指定保存路径!");
		}

		// 创建保存文件路径如果不存在
		File saveFileDirTemp = new File(saveFileDir);
		if (!saveFileDirTemp.exists()) {
			saveFileDirTemp.mkdirs();
		}

		// 处理文件名称
		if (null == newFileName || newFileName.isEmpty()) {
			newFileName = downloadFilePath.substring(downloadFilePath.lastIndexOf("/") + 1, downloadFilePath.length());
		}

		// 创建固定大小的线程池
		ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
		try {
			// 根据文件长度计算合理线程数开始
			URLConnection urlConnection = new URL(downloadFilePath).openConnection();
			// 获取文件长度
			int downloadFileLength = urlConnection.getContentLength();
			// 计算每个线程负责的文件字节长度
			int averageThreadLength = downloadFileLength / threadCount;
			int residueThreadLength = downloadFileLength % threadCount;
			// 让每一个线程开始工作
			int startIndex = 0;
			int endIndex = 0;
			for (int i = 0; i < threadCount; i++) {

				// 计算每一个线程开始和计数索引
				startIndex = i * averageThreadLength;
				// 如果是最后一个线程,则将剩余的全部下载
				if ((i + 1) == threadCount) {
					endIndex = (i + 1) * averageThreadLength + residueThreadLength - 1;
				}
				endIndex = (i + 1) * averageThreadLength - 1;

				// 创建下载线程对象
				DownloadHandlerThread downloadHandlerThread = new DownloadHandlerThread();
				downloadHandlerThread.setDownloadFilePath(downloadFilePath);
				downloadHandlerThread.setSaveFilePath(saveFileDir + newFileName);
				downloadHandlerThread.setStartIndex(startIndex);
				downloadHandlerThread.setEndIndex(endIndex);
				threadPool.execute(downloadHandlerThread);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭线程池
			threadPool.shutdown();
		}
	}
}



 

package com.bochao.download;

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 下载文件处理线程
 * 
 * @Author DuanCZ
 * @Date 2015年9月30日-上午11:38:42
 */

public class DownloadHandlerThread implements Runnable {

    // 待下载的HTTP文件路径
    private String downloadFilePath = null;
    // 下载保存文件路径
    private String saveFilePath = null;
    // 文件随机写入开始索引
    private int startIndex = 0;
    // 文件随机写入结束索引
    private int endIndex = 0;

    @Override
    public void run() {

        System.out.println("线程名称[" + Thread.currentThread().getName() + "]于时间["
                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "]开始下载....");

        // 文件输入流对象
        InputStream fileInputStream = null;
        // 随机访问文件
        RandomAccessFile randomAccessFile = null;

        // ------------------------------------------------------------------------------
        // 获取随机文件流开始
        // 获取一个URL打开链接对象
        URLConnection urlConnection = null;
        try {
            urlConnection = new URL(downloadFilePath).openConnection();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        // 设置该链接允许和用户交互
        urlConnection.setAllowUserInteraction(true);
        // 设置请求属性字节范围
        urlConnection.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex + "");
        try {
            // 获取指定的文件流
            fileInputStream = urlConnection.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // ------------------------------------------------------------------------------获取随机文件流结束

        // ------------------------------------------------------------------------
        // 写文件流到指定的文件开始
        try {
            // 创建文件随机访问对象
            randomAccessFile = new RandomAccessFile(saveFilePath, "rw");
            // 将文件写入位置移动到其实点
            randomAccessFile.seek(startIndex);
            // 写入文件
            int bytes = 0;
            byte[] buffer = new byte[100 * 1024];
            while ((bytes = fileInputStream.read(buffer, 0, buffer.length)) != -1) {
                randomAccessFile.write(buffer, 0, bytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != randomAccessFile) {
                    randomAccessFile.close();
                }
                if (null != fileInputStream) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // ------------------------------------------------------------------------写文件流到指定的文件结束

        System.out.println("线程名称[" + Thread.currentThread().getName() + "]于时间["
                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "]下载完成!");
    }

    public String getDownloadFilePath() {
        return downloadFilePath;
    }

    public void setDownloadFilePath(String downloadFilePath) {
        this.downloadFilePath = downloadFilePath;
    }

    public String getSaveFilePath() {
        return saveFilePath;
    }

    public void setSaveFilePath(String saveFilePath) {
        this.saveFilePath = saveFilePath;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getEndIndex() {
        return endIndex;
    }

    public void setEndIndex(int endIndex) {
        this.endIndex = endIndex;
    }

}



测试:

 

 

package com.bochao.download;

public class TestDownload {

	public static void main(String[] args) {
		
	    String downloadFilePath = "http://localhost:81/mulitThreadDownload/file/PowerDesigner165_Evaluation.1428562995.exe";
		String saveFileDir= "f:\\Download\\";
		
		HttpMulitThreadDownload httpMulitThreadDownload = new HttpMulitThreadDownload(2, downloadFilePath, saveFileDir, "powerdesigner.exe");
		httpMulitThreadDownload.MulitThreadDownload();
	}

}


输出结果:

 

线程名称[pool-1-thread-1]于时间[2015-08-30 16:09:49]开始下载....
线程名称[pool-1-thread-2]于时间[2015-08-30 16:09:49]开始下载....
线程名称[pool-1-thread-2]于时间[2015-09-30 16:09:01]下载完成!
线程名称[pool-1-thread-1]于时间[2015-09-30 16:09:23]下载完成!


 

posted @ 2015-09-30 16:10  dcz1001  阅读(926)  评论(0编辑  收藏  举报