这是我以前copy的关于多线程下载代码,来源我忘记了,我进行了注解。个人认知,不详细之处还请谅解

这是源码:

package teachercode;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class MultyThreadDownload {

 static class DownloadTask implements Runnable {
  private long start;
  private long end;
  private String path;
  private File destFile;

  public DownloadTask(long start, long end,String path, File destFile) {
   this.start = start;
   this.end = end;
   this.path = path;
   this.destFile = destFile;
  }

  @Override
  public void run() {
   RandomAccessFile dest = null;
   InputStream is = null;
   HttpURLConnection conn = null;
   try {
    // 4.线程读写
    URL url = new URL(path);
    conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    is = conn.getInputStream();
    is.skip(start);// 设置源的开始位置

    dest = new RandomAccessFile(destFile, "rw");
    dest.seek(start);// 设置写开始的位置
    byte b[] = new byte[1000];//1.设置数组的大小  2.
    long total = start;//记录上一次下载的位置
    int len = 0;
    // start + len
    while ((len = is.read(b)) > 0) {
     total = total + len;
     if (total > end) {
      len = (int) (len - (total - end));
      dest.write(b, 0, len);
      break;
     } else {
      dest.write(b, 0, len);
     }
    }
    System.out.println(Thread.currentThread().getName()+"完成了下载,下载的数据范围是"+":["+start+","+end+")");
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   } finally {
    if(conn != null){
     conn.disconnect();
    }
    if (is != null) {
     try {
      is.close();
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
    if (dest != null) {
     try {
      dest.close();
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }
  }
 }

  public static void main(String[] args) {
   RandomAccessFile dest = null;
   try {
    // 1.获得服务器文件的大小,在本地新建一个相同大小的文件
    // 服务器文件
    String path = "http://n.sinaimg.cn/transform/20150806/4mXJ-fxftkps3371034.jpg";
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
//    InputStream is = conn.getInputStream();//关联服务器上的文件输入流
    // 在本地新建一个文件和服务器大小是一样
    String filestr = path.substring(path.lastIndexOf("/") + 1);
    File dir = new File("f:\\temp\\download");
    if(!dir.exists()){
     dir.mkdir();//创建目录
    }
    File destFile = new File(dir,filestr);//要写入的文件
    dest = new RandomAccessFile(destFile,"rw");
    long length =  conn.getContentLength();//服务器文件的长度
    dest.setLength(length);//设置本地文件的长度
    // 2.决定要开几个线程下载
    int num = 3;
    long block =length / num;// length = 10 block=3
    long start, end;
    // 3.计算每个线程开始和结束的位置,开线程下载
    for (int i = 0; i < num; i++) {
     start = block * i;
     if (i == num - 1) {
      end = length;
     } else {
      end = start + block;
     }
     // 开线程并启动
     new Thread(new DownloadTask(start, end, path, destFile)).start();
    }
   } catch (IOException e) {
    e.printStackTrace();
   } finally {
    if (dest != null) {
     try {
      dest.close();
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }
  }
}

具体注解:

1. 首先获取服务器文件的大小,在本地新建一个相同大小的文件。
String path = "http://n.sinaimg.cn/transform/20150806/4mXJ-fxftkps3371034.jpg";
URL url = new URL(path);
获得所要下载的文件路径path,将path传到URL实例url中,

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
onn.setRequestMethod("GET");
然后获取url.openConnection   赋给HttpURLConnection实例conn中
通过conn.setRequestMethod(“GET”)请求获取静态页面

String filestr = path.substring(path.lastIndexOf("/") + 1);
创建一个filestr来获得path地址中最后一个”/”之后的数据;

File dir = new File("f:\\temp\\download");
if(!dir.exists()){
   dir.mkdir();//创建目录
}
File destFile = new File(dir,filestr);//要写入的文件
dest = new RandomAccessFile(destFile,"rw");
通过File 创建目录,建立文件destFile,使用RandomAccessFile对创建的文件(destFile)进行读写操作,赋给dest。

long length = conn.getContentLength();
dest.setLength(length);
通过conn的getContentLength()方法获取服务器文件长度。再通过dest中的setLength(getContentLength())方法对本地文件设置长度。

2. 多线程下载分块操作
int num = 3;
long block =length/num;
定义线程数目num,使用文件长度length对num整除获取分区块数。

long start, end;
for (int i = 0; i < num; i++) {
   start = block * i;
   if (i == num - 1) {
    end = length;
   } else {
    end = start + block;
     }
  new Thread(new DownloadTask(start, end, path, destFile)).start();
}
计算每个线程的开始(start)和结束(end)位置,用for循环依次对线程分配下载的开始和结束位置,开始为:块数大小(block)*序号(i)。
第一次开始位置:0        第一次结束位置:end=0+block;
第二次开始位置:block     第二次结束位置:end=block+block;
第三次开始位置:2*block   第三次结束位置:end=length;
由于是采用”/”整除方式,最后一块区域可能放不下文件数据。
所以当下载最后块数(第三块区域)时候,有可能所下载的长度大于 块数长度,这时候要将end为length。

 
3. 线程启动,线程开始对文件读写
线程启动代码:new Thread(new DownloadTask(start, end, path, destFile)).start();
通过for循环 对每个线程开启,并将start,end,path,File destFile,传入DownloadTask中(实现Runnable接口),重写Run方法:在run里完成对文件读写。

URL url = new URL(path);
HttpURLConnection  conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
获得所要下载的文件路径path,将path传到URL实例url中,
然后获取url.openConnection   赋给HttpURLConnection实例conn中
通过conn.setRequestMethod(“GET”)请求获取静态页面

InputStream is = conn.getInputStream();
is.skip(start);
创建输入流is,关联coon的文件输入流。用输入流is方法中的skip(start),设置源的开始位置。

dest = new RandomAccessFile(destFile, "rw");
dest.seek(start);
通过RandomAccessFile对目的文件destFile读写操作:使用seek(start)定位到写 开始位置。然后开始写入数据。

byte b[] = new byte[1000];
long total = start;
int len = 0;
while ((len = is.read(b)) > 0) {
     total = total + len;
     if (total > end) {
      len = (int) (len - (total - end));
      dest.write(b, 0, len);
      break;
     } else {
      dest.write(b, 0, len);
     }
    }
设置读取数组的大小b[]=byte[1000];
While()循环写入数据,开始位置为start,结束位置为end,每读取一次(1000)记录一下上次下载的位置(total ),方法:total = total+len,len为读取长度,初始化为0,write(b,0,len);当total>end(即:说明最后读取长度超过 本块区域(因为文件平分三块)),最后一点区域写入方法:len = len-(total - end)然后write(b,0,len).写入数据。

System.out.println(Thread.currentThread().getName()+"完成了下载,下载的数据范围是"+":["+start+","+end+")");
这段代码测试作用:最后为了方便查看哪个线程下载了哪段区域,使用Thread.currentThread().getName()获得当前线程名字[start,end)  获取区域。










Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值