一、多线程下载

        多线程下载就是抢占服务器资源
        原理:服务器CPU 分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源。
        
       1、设置开启线程数,发送http请求到下载地址,获取下载文件的总长度
          然后创建一个长度一致的临时文件,避免下载到一半存储空间不够了,并计算每个线程下载多少数据       
       2、计算每个线程下载数据的开始和结束位置
          再次发送请求,用 Range 头请求开始位置和结束位置的数据
       3、将下载到的数据,存放至临时文件中
       4、带断点续传的多线程下载
          定义一个int变量,记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件,当文件下载完成,删除临时进度文件。
 public class MultiDownload {

     static int ThreadCount = 3;
static int finishedThread = 0;
//确定下载地址
static String filename = "EditPlus.exe";
static String path = "http://192.168.3.45:8080/"+filename;
public static void main(String[] args) {
//1、发送get请求,去获得下载文件的长度
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000); if (conn.getResponseCode()==200) {
//如果请求成功,拿到所请求资源文件的长度
int length = conn.getContentLength(); //2、生成一个与原文件同样的大小的临时文件,以免下载一半存储空间不够了
File file = new File(filename);//演示,所以将保存的文件目录放在工程的同目录
//使用RandomAccessFile 生成临时文件,可以用指针定位文件的任意位置,
//而且能够实时写到硬件底层设备,略过缓存,这对下载文件是突然断电等意外是有好处的
RandomAccessFile raf = new RandomAccessFile(file, "rwd");//rwd, 实时写到底层设备
//设置临时文件的大小
raf.setLength(length);
raf.close(); //3、计算出每个线程应该下载多少个字节
int size = length/ThreadCount;//如果有余数,负责最后一部分的线程负责下砸 //开启多线程
for (int threadId = 0; threadId < ThreadCount; threadId++) {
//计算每个线程下载的开始位置和结束位置
int startIndex = threadId*size; // 开始 = 线程id * size
int endIndex = (threadId+1)*size - 1; //结束 = (线程id + 1)*size - 1
//如果是最后一个线程,那么结束位置写死为文件结束位置
if (threadId == ThreadCount - 1) {
endIndex = length - 1;
} //System.out.println("线程"+threadId+"的下载区间是: "+startIndex+"----"+endIndex);
new DownloadThread(startIndex,endIndex,threadId).start();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} class DownloadThread extends Thread{ private int startIndex;
private int endIndex;
private int threadId; public DownloadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
} @Override
public void run() {
//每个线程再次发送http请求,下载自己对应的那部分数据
try {
File progressFile = new File(threadId+".txt");
//判断进度文件是否存在,如果存在,则接着断点继续下载,如果不存在,则从头下载
if (progressFile.exists()) {
FileInputStream fis = new FileInputStream(progressFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
//从进度文件中度取出上一次下载的总进度,然后与原本的开始进度相加,得到新的开始进度
startIndex += Integer.parseInt(br.readLine());
fis.close();
}
System.out.println("线程"+threadId+"的下载区间是:"+startIndex+"----"+endIndex); //4、每个线程发送http请求自己的数据
URL url = new URL(MultiDownload.path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//设置本次http请求所请求的数据的区间
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex); //请求部分数据,响应码是206
if (conn.getResponseCode()==206) {
//此时,流里只有ThreadCount分之一的原文件数据
InputStream is = conn.getInputStream();
byte[] b = new byte[1024];
int len = 0;
int total = 0;//total 用于保存断点续传的断点
//拿到临时文件的输出流
File file = new File(MultiDownload.filename);
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//把文件的写入位置移动至 startIndex
raf.seek(startIndex); while ((len = is.read(b))!=-1) {
//每次读取流里数据之后,同步把数据写入临时文件
raf.write(b, 0, len);
total += len; //System.out.println("线程" + threadId + "下载了" + total);
//生成一个一个专门用来记录下载进度的临时文件
RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
progressRaf.write((total+"").getBytes());
progressRaf.close();
}
System.out.println("线程"+threadId+"下载完了---------------------");
raf.close(); //当所有的线程下载完之后,将进度文件删除
MultiDownload.finishedThread++;
synchronized (MultiDownload.path) {//所有线程使用同一个锁
if (MultiDownload.finishedThread==MultiDownload.ThreadCount) {
for (int i = 0; i < MultiDownload.ThreadCount; i++) {
File f = new File(i+".txt");
f.delete();
}
MultiDownload.finishedThread=0;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} }
}
二、Android手机版带断点续传的多线程下载
     Android手机版的带断点续传的多线程下载逻辑与PC版的几乎一样,只不过在Android手机中耗时操作不能放在主线程,网络下载属于耗时操作,所以多线程下载要在Android中开启一个子线程执行。并使用消息队列机制刷新文本进度条。
public class MainActivity extends Activity {

    static int ThreadCount = 3;
static int FinishedThread = 0;
int currentProgess;
static String Filename = "QQPlayer.exe";
static String Path = "http://192.168.3.45:8080/"+Filename; static MainActivity ma;
static ProgressBar pb;
static TextView tv;
static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg){
tv.setText((long)pb.getProgress()*100 /pb.getMax() +"%");
};
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ma = this;
pb = (ProgressBar) findViewById(R.id.pb);
tv = (TextView) findViewById(R.id.tv);
} public void download(View v){ Thread t = new Thread(){ @Override
public void run() {
//发送http请求获取文件的长度,创建临时文件
try {
URL url= new URL(Path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000); if (conn.getResponseCode()==200) {
int length = conn.getContentLength(); //设置进度条的最大值就是原文件的总长度
pb.setMax(length); //生成一个与原文件相同大小的临时文件
File file = new File(Environment.getExternalStorageDirectory(),Filename);
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.setLength(length);
raf.close(); //计算每个线程需要下载的数据大小
int size = length/ThreadCount;
//开启多线程
for (int threadId = 0; threadId < ThreadCount; threadId++) {
int startIndex = threadId*size;
int endIndex = (threadId + 1)*size - 1;
if (threadId==ThreadCount - 1) {
endIndex = length - 1;
} new DownloadThread(startIndex, endIndex, threadId).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
} class DownloadThread extends Thread{
private int startIndex;
private int endIndex;
private int threadId; public DownloadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
} @Override
public void run() {
// 每个线程发送http请求自己的数据
try{
//先判断是不是断点续传
File progessFile = new File(Environment.getExternalStorageDirectory(),threadId+".txt");
if (progessFile.exists()) {
FileReader fr = new FileReader(progessFile);
BufferedReader br = new BufferedReader(fr);
int lastProgess = Integer.parseInt(br.readLine());
startIndex += lastProgess; //把上次下载的进度显示至进度条
currentProgess +=lastProgess;
pb.setProgress(currentProgess);
//发消息,让主线程刷新文本进度
handler.sendEmptyMessage(1); br.close();
fr.close();
} URL url = new URL(Path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex); if (conn.getResponseCode()==206) {
InputStream is = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
int total = 0; File file = new File(Environment.getExternalStorageDirectory(),Filename);
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex); while ((len = is.read(buffer))!= -1) {
raf.write(buffer, 0, len);
total += len; //每次读取流里数据之后,把本次读取的数据的长度显示至进度条
currentProgess += len;
pb.setProgress(currentProgess);
//发消息,让主线程刷新文本进度
handler.sendEmptyMessage(1);
//生成临时文件保存下载进度,用于断点续传,在所有线程现在完毕后删除临时文件
RandomAccessFile progressRaf = new RandomAccessFile(progessFile, "rwd");
progressRaf.write((total+"").getBytes());
progressRaf.close();
}
raf.close();
System.out.println("线程"+threadId+"下载完了"); //当所有线程都下在完了之后,删除临时进度文件
FinishedThread++;
synchronized (Path) {
if (FinishedThread==ThreadCount) {
for (int i = 0; i < ThreadCount; i++) {
File f = new File(Environment.getExternalStorageDirectory(),i+".txt");
f.delete();
}
FinishedThread=0;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

网络编程之PC版与Android手机版带断点续传的多线程下载的更多相关文章

  1. Android网络编程之HttpClient运用

    Android网络编程之HttpClient运用 在 Android开发中我们经常会用到网络连接功能与服务器进行数据的交互,为此Android的SDK提供了Apache的HttpClient来方便我们 ...

  2. Android 网络编程之HttpURLConnection运用

    Android 网络编程之HttpURLConnection 利用HttpURLConnection对象,我们可以从网络中获取网页数据. 01 URL url = new URL("http ...

  3. unix下网络编程之I/O复用(三)

    poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...

  4. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

  5. [深入浅出WP8.1(Runtime)]网络编程之HttpClient类

    12.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,我们还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类 ...

  6. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...

  7. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  8. 网络编程之UDP编程

    网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了Data ...

  9. 网络编程之TCP编程

    网络编程之TCP编程 前面已经介绍过关于TCP协议的东西,这里不做赘述.Java对于基于TCP协议的网络通信提供了良好的封装,Java使用socket对象来代表两端的通信窗口,并通过Socket产生I ...

随机推荐

  1. linux系统中内存爆满之后会如何?

    在使用python写程序的时候,发现一个可以无限迭代的迭代器,从而可以直接将系统中的内存占满,那么占满之后会发生什么呢? 1. 创建无限迭代,生成列表,如下: [root@python ~]# pyt ...

  2. Sunday算法(字符串查找、匹配)

    字符串查找算法中,最著名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore).两个算法在最坏情况下均具有线性的查找时间.但是在实用上,KMP算法并不比最简单的 ...

  3. C++设计模式——单例模式

    问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...

  4. java 开发环境

    jdk:包括jre,自己下载即可. 客户端只需安装jre即可. 安装路径:C:\jdk7.0\jdk1.7.0_25\bin (适时更改) 环境变量是从前往后找 测试成功:cmd      java ...

  5. Delphi 延迟函数 比sleep 要好的多

    转自:http://www.cnblogs.com/Bung/archive/2011/05/17/2048867.html //延迟函数:方法一 procedure delay(msecs:inte ...

  6. Codeforces Educational Codeforces Round 15 C. Cellular Network

    C. Cellular Network time limit per test 3 seconds memory limit per test 256 megabytes input standard ...

  7. 轻松学习Linux之如何创建可执行脚本

    本文出自 "李晨光原创技术博客" 博客,谢绝转载!

  8. 使用 XMPP 构建一个基于 web 的通知工具——转

    Inserting of file(使用 XMPP 构建一个基于 web 的通知工具.docx) failed. Please try again. http://www.ibm.com/develo ...

  9. 转】用Maven构建Mahout项目

    原博文出自于: http://blog.fens.me/hadoop-mahout-maven-eclipse/ 感谢! 用Maven构建Mahout项目 Hadoop家族系列文章,主要介绍Hadoo ...

  10. Swift 可选值(Optional Values)介绍

    Optional的定义 Optional也是Objective-C没有的数据类型,是苹果引入到Swift语言中的全新类型,它的特点就和它的名字一样:可以有值,也可以没有值,当它没有值时,就是nil.此 ...