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

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]下载完成!

Java多线程文件下载的更多相关文章

  1. Android实现网络多线程文件下载

    实现原理 (1)首先获得下载文件的长度,然后设置本地文件的长度. (2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置. 如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M ...

  2. Java多线程的下载器(1)

    实现了一个基于Java多线程的下载器,可提供的功能有: 1. 对文件使用多线程下载,并显示每时刻的下载速度. 2. 对多个下载进行管理,包括线程调度,内存管理等. 一:单个文件下载的管理 1. 单文件 ...

  3. 简述Java多线程(一)

    JAVA多线程 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念. 进程:是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位. 线程是CPU调度和执行的单位. 创 ...

  4. 40个Java多线程问题总结

    前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...

  5. Java多线程基础知识篇

    这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...

  6. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  7. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  8. Java多线程--让主线程等待子线程执行完毕

    使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...

  9. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

随机推荐

  1. JavaScript之闭包

    JavaScript之闭包 在JavaScript中,闭包恐怕是很多人不能理解的一个概念了,甚至很多人也会把闭包和匿名函数混淆. 闭包是有权访问另一个函数作用域中的变量的函数.首先要明白的就是,闭包是 ...

  2. linux安装ftp组件

    1   安装vsftpd组件 linux系统安装完后,有/etc/vsftpd/vsftpd.conf 文件,是vsftp的配置文件. [root@bogon ~]# yum -y install v ...

  3. Objective-C之字典

    //字典:(关键字 值) //插入代码字太小 //        NSArray *array = [NSArray array];//空数组 //        NSDictionary *dict ...

  4. OC之NSString、NSMutableString学习笔记 常用方法

    NSString篇: 1.字符串连接 NSString *beijing = @"北京"; NSString *welcome = [beijing stringByAppendi ...

  5. UML的目标

    http://zhidao.baidu.com/link?url=ghQvzG70vSCSLyQcrHDTd7xt1aSWBR73lPIMxBCEPo1ktkq9cQ3EE9TXX1mZyHINkVA ...

  6. 【原】webpack学习笔记

    之前在react的项目中有用过webpack,不过没有认真的去研究,这段时间又重新好好的学习一下webpack,发觉 webpack是一个很强大的东西.而且很好用,方便,接下来主要是做一下学习的笔记 ...

  7. owin

    app.Properties["Hello"] = System.DateTime.Now; app.Run(async context => await context.R ...

  8. mssql注入

    <%@ Page Language="C#" AutoEventWireup="true" %> <%@ Import Namespace=& ...

  9. Windows溢出提权小结

    1.  查看系统打补丁情况:systeminfo 2.  查看KB-EXP表: KB2360937 MS10-084 KB2478960 MS11-014 KB2507938 MS11-056 KB2 ...

  10. yii2 Pjax的使用

    有两个例子:刷新时间和数据显示排序 1.刷新时间 (1)控制器中的方法:Time public function actionTime() { return $this->render('tim ...