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

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. python 培训之HTTP

    1. urllib #!/usr/env/python # -*- coding:UTF-8 -*- from __future__ import print_function import sys ...

  2. IBatis分页显示

    <select id="pagedListOrderOpen"> SELECT * FROM ( </select> <sql id="pa ...

  3. mysqli连接数据库的模板

    <?php $host="localhost"; $db_user="root"; //数据库用户 $db_pass=""; //数据 ...

  4. Apache+php+mysql+SQLyog在windows7下的安装与配置图解

    先准备好软件: Apache官方下载地址:httpd-2.2.25-win32-x86-openssl-0.9.8y.msi,更多版本在这里: php官方下载地址:php-5.4.37-Win32-V ...

  5. vim 快捷键 以及技巧

    [root@centos01 biji]# vim + 1.txt 打开文件,光标定位到最后一行[root@centos01 biji]# vim +5 1.txt 打开文件,光标定位到第5行[roo ...

  6. 第一章 基础设施,1.3 阿里视频云ApsaraVideo是怎样让4000万人同时狂欢的(作者:蔡华)

    1.3 阿里视频云ApsaraVideo是怎样让4000万人同时狂欢的 前言 在今年的双11中,双11天猫狂欢夜的直播成为一大亮点. 根据官方披露数据,直播总观看人数超4257万,同时观看人数峰值达5 ...

  7. Android studio 的初始设置

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  8. 新版WampServer项目路径前面没有localhost

    1.第一种情况:http://localhost/页面中不能访问 解决方式: 在www目录下找到index.php 然后修改里面查找$projectContents 或直接查看338行代码 修改'ht ...

  9. sufeinet

    http://www.sufeinet.com http://tool.sufeinet.com/

  10. Web启动服务器上的某一个服务

    情景是这样的.. 网页打开一个数据列表..数据要求实时从其他多个平台上获取.. 所以就有了一个Web页面..还有个WinService的服务来定时获取这些数据... 问题来了.. 发现Service有 ...