分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

前面写了单线程下载、断点续传、文件分隔与合并三个程序(具体可以参见我前面的程序),在这个程序的基础之上,我完成了多线程下载程序,并具备断点续传功能。

该程序具有5个文件:Main.java(主文件)、FileCombination.java(临时文件合并)、GetFileThread.java(网络文件获取)、MultiThreadGetFile.java(多线程下载调度程序)、PoliceThread.java(监视线程,确定所有的文件块都完成,并调用文件合并程序)。文件详细如下:

Main.java:

package MultiThread;
/**
 * 程序主文件
 */
public class Main
{
  String urlFile;//网络文件地址
  int threadNum;//要启动下载的线程数
  String localFileAddress;//要保存的本地地址,请保重该处没有名为"tmp"的文件夹
  public Main()
  {
    /**
     * 下面的由使用者自己设为定
     */
    urlFile="http://www.netbox.cn/download/nbsetup.EXE";
    threadNum=9;//要同时下载的线程数
    localFileAddress="d://multiDownTest//";
  }
  private void start()
  {
    Thread thread=new Thread(new MultiThreadGetFile(urlFile,threadNum,localFileAddress));
    thread.start();
  }
  public static void main(String[] args)
  {
    Main main = new Main();
    main.start();
  }
}

FileCombination.java:

package MultiThread;
/**
 * 合并文件:合并由拆分文件拆分的文件
 * 要求将拆分文件放到一个文件夹中
 * 主要利用随机文件读取和文件输入输出流
 */
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

import java.util.Arrays;
import java.util.StringTokenizer;

public class FileCombination extends Thread
{
  String srcDirectory=null;//拆分文件存放的目录
  String trueDirectory;//结果文件存放目录
  String[] separatedFiles;//存放所有拆分文件名
  String[][] separatedFilesAndSize;//存放所有拆分文件名及分件大小
  int FileNum=0;//确定文件个数
  String fileRealName="";//据拆分文件名确定现在原文件名
  public FileCombination(String trueDirectory,String srcDirectory)
  {
    this.srcDirectory=srcDirectory;
    this.trueDirectory=trueDirectory;
  }
  /**
   *
   * @param sFileName 任一一个拆分文件名
   * @return 原文件名
   */
  private String getRealName(String sFileName)
  {
    StringTokenizer st=new StringTokenizer(sFileName,".");
    return st.nextToken()+"."+st.nextToken();
  }
  /**
   * 取得指定拆分文件模块的文件大小
   * @param FileName 拆分的文件名
   * @return
   */
  private long getFileSize(String FileName)
  {
    FileName=srcDirectory+"//"+FileName;
    return (new File(FileName).length());
  }
  /**
   * 生成一些属性,做初使化
   * @param drictory 拆分文件目录
   */
  private void getFileAttribute(String drictory)
  {
    File file=new File(drictory);
    separatedFiles=new String[file.list().length];//依文件数目动态生成一维数组,只有文件名
    separatedFiles=file.list();
    //依文件数目动态生成二维数组,包括文件名和文件大小
    //第一维装文件名,第二维为该文件的字节大小
    separatedFilesAndSize=new String[separatedFiles.length][2];
    Arrays.sort(separatedFiles);//排序
    FileNum=separatedFiles.length;//当前文件夹下面有多少个文件
    for(int i=0;i<FileNum;i++)
    {
      separatedFilesAndSize[i][0]=separatedFiles[i];//文件名
      separatedFilesAndSize[i][1]=String.valueOf(getFileSize(separatedFiles[i]));//文件大上
    }
    fileRealName=getRealName(separatedFiles[FileNum-1]);//取得文件分隔前的原文件名
  }
  /**
   * 合并文件:利用随机文件读写
   * @return true为成功合并文件
   */
  private boolean CombFile()
  {
    RandomAccessFile raf=null;
    long alreadyWrite=0;
    FileInputStream fis=null;
    int len=0;
    byte[] bt=new byte[1024];
    try
    {
      raf = new RandomAccessFile(trueDirectory+"//"+fileRealName,"rw");
      for(int i=0;i<FileNum;i++)
      {
        raf.seek(alreadyWrite);
        //System.out.println("alreadyWrite:"+alreadyWrite);
        fis=new FileInputStream(srcDirectory+"//"+separatedFilesAndSize[i][0]);
        while((len=fis.read(bt))>0)
        {
          raf.write(bt,0,len);
        }
        fis.close();
        alreadyWrite=alreadyWrite+Long.parseLong(separatedFilesAndSize[i][1]);
      }
      raf.close();     
    }
    catch (Exception e)
    {
      e.printStackTrace();
      try
      {
        if(raf!=null)
          raf.close();
        if(fis!=null)
          fis.close();
      }
      catch (IOException f)
      {
        f.printStackTrace();
      }
      return false;
    }
    return true;
  }
  public void deleteTmp()
  {
    for(int i=0;i<FileNum;i++)
    {
      File file=new File(srcDirectory+"//"+separatedFilesAndSize[i][0]);
      file.delete();
    }
    File file1=new File(srcDirectory);
    file1.delete();
  }
  public void run()
  {
    getFileAttribute(srcDirectory);
    CombFile();
    deleteTmp();
  }
}
GetFileThread.java:

package MultiThread;
/**
 * 下载线程
 * 原理:
 *    根据传入的下载开始点以及文件下载结束点,利用HttpURLConnection的RANGE属性,
 *    从网络文件开始下载,并结合判断是否下载的文件大小已经等于(文件下载结束点-下载开始点), *   
 *    这里结合断点续传原理,可以更快、更有效的下载文件
 */
import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.net.*;

public class GetFileThread extends Thread
{
  long startPos,endPos;//传入的文件下载开始、结束点
  String currentFileThreadName;//要带上完整的路径
  String urlFile;//网络文件地址
  int currentThread;//当前是那个线程,这主要是用于下载完成后将对应的检测标志设为true,表示下载完成
  /**
   *
   * @param urlFile 网络文件地址
   * @param startPos 网络开始下载点
   * @param endPos 网络文件结点
   * @param currentFileThreadName 当前线程的完程路径及名字
   * @param currentThread 当前是第几个线程
   */
  public GetFileThread(String urlFile,long startPos,long endPos,String currentFileThreadName,int currentThread)
  {
    this.startPos=startPos;
    this.endPos=endPos;
    this.currentFileThreadName=currentFileThreadName;
    this.urlFile=urlFile;
    this.currentThread=currentThread;
  }
  private boolean FileExist(String pathAndFile)
  {
    File file = new File(pathAndFile);
    if (file.exists())
      return true;
    else
      return false;
  }

private long FileSize(String pathAndFile)
  {
    long fileSize=0;
    File filet = new File(pathAndFile);
    fileSize=filet.length();
    return fileSize;
  }

private void FileRename(String fName, String nName)
  {
    File file = new File(fName);
    file.renameTo(new File(nName));
    file.delete();
  }

public void run()
  {
    URL url = null;
    HttpURLConnection httpURLConnection = null;
    DataOutputStream dos = null;
    BufferedInputStream bis = null;
    FileOutputStream fos = null;
    String localFile = currentFileThreadName; //文件保存的地方及文件名,具体情况可以改
    String localFile_tp = localFile + ".tp"; //未下载完文件加.tp扩展名,以便于区别
    long fileSize = 0;//在断点续传中,用于取得当前文件已经下载的大小
    int len = 0;
    byte[] bt = new byte[1024];//缓冲区
    //byte[] buffer=new byte[50*1024];
    RandomAccessFile raFile = null;
    long TotalSize = 0; //当前块要下载的文件总大小
    try
    {
      url = new URL(urlFile);
      httpURLConnection = (HttpURLConnection) url.openConnection();
      //TotalSize = Long.parseLong(urlc.getHeaderField("Content-Length"));//取得网络文件大小
      TotalSize=endPos-startPos;//取得要该块文件实际要写的大小
      long downSize=0;//已经下载的大小
      //确定临时文件是否存在
      if (FileExist(localFile_tp)) //采用断点续传,这里的依据是看下载文件是否在本地有.tp有扩展名同名文件
      {
        System.out.println("文件续传中...");   
        fileSize=new File(localFile_tp).length();//取得已经下载的大小,以便确定随机写入的位置
        downSize=fileSize;//下载大小
        fileSize=fileSize+startPos;//取得文件开始写入点
        //设置User-Agent
        //urlc.setRequestProperty("User-Agent","NetFox");
        /**
         * httpURLConnection属性的设置一定要在得到输入流之前,否则会报已经连接的错误
         */
        //设置断点续传的开始位置
         //synchronized(new Object()){       
        httpURLConnection.setRequestProperty("RANGE", "bytes=" + fileSize + "-");
        //urlc.setRequestProperty("RANGE", "bytes="+fileSize);//这样写不行,不能少了这个"-".
        //设置接受信息
        httpURLConnection.setRequestProperty("Accept",
                                "image/gif,image/x-xbitmap,application/msword,*/*");
         //}
        raFile = new RandomAccessFile(localFile_tp, "rw"); //随机方位读取
        raFile.seek(downSize); //定位指针到fileSize位置
        bis = new BufferedInputStream(httpURLConnection.getInputStream());
        while ((len = bis.read(bt)) > 0)
        {         
          if(downSize<(endPos-startPos))
          {           
            downSize=downSize+len;
            if(downSize>(endPos-startPos))
            {
              len=(int)((endPos-startPos)-(downSize-len));
            }          
            raFile.write(bt, 0, len);
          }
          else
            break;         
        }
        //System.out.println("文件续传接收完毕!");
      }
      else if(!FileExist(localFile))//采用原始下载,但保证该文件没有下载
      {       
        //设置断点续传的开始位置
        httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-");
        bis = new BufferedInputStream(httpURLConnection.getInputStream());       
        fos = new FileOutputStream(localFile_tp); //没有下载完毕就将文件的扩展名命名.tp
        dos = new DataOutputStream(fos);
        //System.out.println("正在接收文件...");
        while ((len = bis.read(bt)) > 0)
        {         
          if(downSize<(endPos-startPos))//确定没有下载完毕
          {
            downSize=downSize+len;
            if(downSize>(endPos-startPos))//如果当前下载的加上要下载的已经超过要求的下载范围
            {
              len=(int)((endPos-startPos)-(downSize-len));//就只取满足要求的下功部份
            }           
            dos.write(bt, 0, len);//写文件
          }
          else
            break;
        }
      }
      if (bis != null)
        bis.close();
      if (dos != null)
        dos.close();
      if (fos != null)
        fos.close();
      if (raFile != null)
        raFile.close();
      //System.out.println("localFile_bak:" + FileSize(localFile_bak));
      if (FileSize(localFile_tp) == TotalSize) //下载完毕后,将文件重命名
      {
        FileRename(localFile_tp, localFile);
      }
      MultiThreadGetFile.checkList[currentThread]=true;
    }
    catch (Exception e)
    {
      try
      {
        if (bis != null)
          bis.close();
        if (dos != null)
          dos.close();
        if (fos != null)
          fos.close();
        if (raFile != null)
          raFile.close();
      }
      catch (IOException f)
      {
        f.printStackTrace();
      }
      e.printStackTrace();
    }
  }
}
MultiThreadGetFile.java:

package MultiThread;
/**
 * 多线程下载调度程序
 */
import java.io.File;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.StringTokenizer;

public class MultiThreadGetFile extends Thread
{
  long startPos=0,endPos=0;
  String currentFileThreadName;//要带上完整的路径
  String urlFile;//网络文件地址
  String urlFileName;//网络文件名
  String localFileAddress;//下载文件要存放的地址
  int threadNum;//要同时下载的线程数
  long[] eachThreadLength;//每个线程要下功的文件分块的大小
  long urlFileLength;//网络文件的大小
  URL url;
  HttpURLConnection httpURLConnection;
  public static boolean[] checkList;//检测线程
  public MultiThreadGetFile(String urlFile,int threadNum,String localFileAddress)
  {
    this.urlFile=urlFile;
    this.threadNum=threadNum;//要同时下载的线程数
    this.localFileAddress=localFileAddress;
   
  }
  private void init_getEachThreadLength()//确定每个线程文件最终要写的文件在大小
  {
    long l;
    l=urlFileLength/threadNum;
    for(int i=0;i<threadNum;i++)
    {
      if(i==threadNum-1)//如果是分配最后一个线程了
      {
        eachThreadLength[i]=urlFileLength-i*l;
      }
      else
        eachThreadLength[i]=l;
    }
  }
  private String GetFileName(String file)
  {
    StringTokenizer st=new StringTokenizer(file,"/");
    while(st.hasMoreTokens())
    {
      file=st.nextToken();
    }
    return file;
  }
  private void init()
  {
   
    if(!new File(localFileAddress+"tmp").mkdir())//创建一个临时文件夹
    {
      System.out.println("创建文件夹失败!");
    }
    eachThreadLength=new long[threadNum];
    try
    {
      url=new URL(urlFile);
      httpURLConnection=(HttpURLConnection)url.openConnection();
      urlFileLength=Long.parseLong(httpURLConnection.getHeaderField("Content-Length"));     
      urlFileName=url.getFile();//取得在服务器上的路径及文件名
      urlFileName=GetFileName(urlFileName);//只得文件名
      init_getEachThreadLength();
      httpURLConnection.disconnect();
      checkList=new boolean[threadNum+1];
      for(int i=1;i<=threadNum;i++)
      {
        if(i>1)
          startPos=startPos+eachThreadLength[i-2];
        endPos=startPos+eachThreadLength[i-1];
        currentFileThreadName=localFileAddress+"tmp//"+urlFileName+".part"+i;
        //System.out.println("startPos:"+(startPos));
        //System.out.println("endPos:"+(endPos));
        //System.out.println("Size:"+(endPos-startPos));
        Thread thread=new Thread(new GetFileThread(urlFile,startPos,endPos,currentFileThreadName,i));
        thread.start();
        checkList[i]=false;//表示该线程开始
      }
      Thread policeThread=new Thread(new PoliceThread(threadNum,localFileAddress,localFileAddress+"tmp"));
      policeThread.start();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  public void run()
  {
    init();
  }
}

PoliceThread.java:

package MultiThread;

/**
 * 监视线程,检测其它的线程是否已经运行完毕
 * 原理:
 *    在MultiThreadGetFile里定义一个全局静态boolean数组,在启动每个GetFileThread
 *    的时候,就将对应的数组的值设为false,当对应线程完成后就把对应的数组设为true,
 *    在当前线程采用不停检测是否所有数组的值都为true,如是那就说明所有的线程已经运行完
 *    毕,如果没有就继续检测。
 *    等到所有的GetFileThread线程都完成后,那么就调用文件拼合线程,合并下载的文件块并删除
 *    临时文件块。
 */
public class PoliceThread
  extends Thread
{
  int totalThread;
  String localFileAddress;
  String localFileAddress_tmp;

public PoliceThread(int totalThread, String localFileAddress,
                      String localFileAddress_tmp)
  {
    this.totalThread = totalThread;
    this.localFileAddress = localFileAddress;
    this.localFileAddress_tmp = localFileAddress_tmp;
  }

public void run()
  {
    boolean isRun = true;
    int allStop = 0;
    while (isRun)
    {
      allStop=0;
      for (int i = 1; i <= totalThread; i++)
      {
        if (MultiThreadGetFile.checkList[i] == true)
        {
          allStop++;
        }
      }
      try
      {
        this.sleep(500);
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
      if (allStop == totalThread)
        isRun = false;
    }
    Thread thread =
      new Thread(new FileCombination(localFileAddress, localFileAddress_tmp));
    thread.start();
  }
}

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

JAVA写的多线程下载程序,并具有断点续传功能相关推荐

  1. java 多线程下载_使用java实现http多线程下载

    Feedback # re: 使用java实现http多线程下载 2008-07-27 22:46 xzqttt 看了您的文章,收到了很大的启发,谢谢分享,好文! 回复  更多评论 # re: 使用j ...

  2. 面试必问:用 Java 写一个内存泄漏程序

    编译:ImportNew/唐尤华 原文链接:stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java 问题: 刚参加的一 ...

  3. java背单词软件_一个JAVA写的背单词程序

    一个JAVA写的背单词程序 2007-6-9文字大小:大中小 俺看了一些Java, 写个程序出来玩玩.由于界面是用Jbuilder生成的,可能代码比较乱,而且还没合起来. 目前版本是0.00001 / ...

  4. 用JAVA写一个画图小程序(JAVA 大作业)

    第一次写博客 且是稍微大点的程序 看看就行 重新写的在这,更加清晰明了:点击进入:用JAVA写一个画图小程序(JAVA 大作业)重排版本 设计思路 首先我直接去了Windows自带画图程序去实践模拟, ...

  5. 用爬虫写一个,小说下载程序

    用爬虫写一个,小说下载程序 环境:python 3.7.3 没啥难度和技术含量,随便写写.本来程序有部分可用format进行优化,但又想了想小说名可能还会 有一些奇奇怪怪的符号,就没弄了. impor ...

  6. JAVA代码实现多线程下载

    首先,我们构建一个多线程下载工具类--DownUtil.代码如下: import java.net.URL; import java.net.HttpURLConnection; import jav ...

  7. python爬虫程序下载_Python爬虫之多线程下载程序类电子书

    近段时间,笔者发现一个神奇的网站:http://www.allitebooks.com/ ,该网站提供了大量免费的编程方面的电子书,是技术爱好者们的福音.其页面如下: ![](https://imag ...

  8. 浅谈Android和java中的多线程下载

    为什么要使用多线程下载呢? 究其原因就一个字:"快",使用多线程下载的速度远比单线程的下载速度要快,说到下载速度,决定下载速度的因素一般有两个: 一个是客户端实际的网速,另一个则是 ...

  9. android多线程下载程序卡死,android 多线程下载与断点续传

    多线程下载: 下载速度更快,服务器对每个线程平分资源,故线程越多,得到的资源越多,下载速度越快. 断点续传: 下载中断,再次下载时从上一次下载结束的位置开始下载,防止重复下载 下载结束后 代码: pa ...

最新文章

  1. 二十二、“此生无悔入华夏,来世还在种花家”(2021.7.1)
  2. 解决Github速度太慢的几种方案
  3. IPv6域名解析服务基础
  4. java args_Java命令行界面(第2部分):args4j
  5. 第三讲系统的基本操作
  6. php页面转发,php如何实现页面路由转发
  7. ISTQB 软件测试资质认证
  8. HDU1106 排序(解法二)(废除!!!)
  9. java session机制_如何学习Session的机制使用
  10. 北京地铁和广州地铁之感想
  11. 如何上传蛋白质组数据
  12. es7 如何从项目resource读取index的mapping、setting.json文件
  13. 《深入理解SPARK:核心思想与源码分析》一书正式出版上市
  14. grafana-reporter
  15. 【点击复制 并自动打开微信添加好友】h5点击复制微信号并自动打开微信添加好友
  16. AE/PR插件AI智能背景抠像颜色键控GoodbyeGreenscreenzxb V1.6.0官方版
  17. the win16 subsystem was unable to enter protected mode,DOSX.EXE must be in your AUTOEXEC.NT and pres
  18. 奥鹏计算机文化基础在线考试,奥鹏中国石油大学北京《计算机文化基础》在线考试客观题答案.doc...
  19. 【Linux 内核 内存管理】分区伙伴分配器 ② ( free_area 空闲区域结构体源码 | 分配标志位 | GFP_ZONE_TABLE 标志位区域类型映射表 |分配标志位对应的内存区域类型 )
  20. uTorrent红种问题(win7)-“错误,拒绝访问”

热门文章

  1. 厚积薄发!华为云7篇论文被AAAI收录,2021年AI行业技术风向标看这里!
  2. ModelArts 与HiLens Kit联合开发丨行人社交距离风险提示Demo
  3. 【华为云技术分享】云小课 | “VPC连接”知多少
  4. 基于Docker搭建分布式消息队列Kafka
  5. ENABLING SPIKE-BASED BACKPROPAGATION FOR TRAINING DEEP NEURAL NETWORK ARCHITECTURES
  6. spring boot整合shiro继承redis_Springboot+Shiro+redis整合
  7. Windows + IDEA + SBT 打造Spark源码阅读环境
  8. IBM 技术文档:Spark, 快速数据分析的又一选择
  9. Node.js抓取网页信息并展示(cheerio网络爬虫)
  10. 给定一个数组 prices计算其利润最大