现在的位置: 首页 > 移动开发 > 正文

Android学习笔记—java实现多线程下载器,30_多线程下载原理介绍和使用

2019年09月20日 移动开发 ⁄ 共 6865字 ⁄ 字号 评论关闭
2013-04-01
30_多线程下载原理
--------------------
a.文件下载原里:
  使用http协议实现多线程下载
b.采用多线程下载,可以抢占服务器cpu的处理时间,实现快速下载
c.使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资 
  源多。如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计 
  算机中并非并发执行,而是由CPU划分时间片轮流执行,如果A应用使用了99条线程下载文件,那么相 
  当于占用了99个用户的资源,假设一秒内CPU分配给每条线程的平均执行时间是10ms,A应用在服务器 
  中一秒内就得到了990ms的执行时间,而其他应用在一秒内只有10ms的执行时间。就如同一个水龙头  
  ,每秒出水量相等的情况下,放990毫秒的水
  肯定比放10毫秒的水要多。
d.多线程下载的实现过程:
  1>首先得到下载文件的长度,然后设置本地文件的长度。
  HttpURLConnection.getContentLength();
  RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");
  file.setLength(filesize);//设置本地文件的长度
  2>根据文件长度和线程数计算每条线程下载的数据长度和下载位置。如:文件的长度为6M,线程数为 
 3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如上图所示。
  3>使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,如:指  
定从文件的2M位置开始下载,下载到位置(4M-1byte)为止,代码如下:
  HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");
  4>保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
  RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");
  threadfile.seek(2097152);//从文件的什么位置开始写入数据
----------------------------------------------------------------------------------
下面是用java项目实现多线程下载的所有代码:
a.新建DownloadTest的javaProject
  /DownloadTest/src/com/credream/download/MulThreadDownLoad.java
package com.credream.download;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class MulThreadDownLoad
{
public static void main(String[] args) throws Exception
{
new MulThreadDownLoad().download(path, 3);//调用下载方法,使用3个线程下载
}
/**
 * 下载文件的方法
 * @param path
 * @param threadsize
 * @throws Exception
 */
private void download(String path ,int threadsize)throws Exception
{
URL url=new URL(path);
HttpURLConnection conn=
(HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if(conn.getResponseCode()==200){
int length=conn.getContentLength();
File file=new File(getFileName(path));//如果不给路径的话,那么默认的保存在
//本项目的目录底下
RandomAccessFile accessFile=new RandomAccessFile(file, "rwd");
    accessFile.setLength(length);
    accessFile.close();
//注意:r以只读方式打开,rw:打开以便于读取和写入
   /*rws:s代表的是文件的元数据,就是指的是文件的大小了等等,当设置为rws的时
   当文件变化的时候,会自动的把文件的内容和元数据同步到手机的设备上
   rwd:表示当文件变化的时候会自动的把文件的内容同步到手机设备上,而不是把文
件的元数据也
   同步到手机设备上.
   为了避免当手机掉电的时候,会丢失数据,那么最好采用rwd,每写一些数据就立即
同步到手机设备上
   不至于数据的损坏
   rw的时候,当没有这个文件的时候会创建这个文件.
   */
   int block=length%threadsize==0? length/threadsize:length/threadsize+1;
for(int threadid=0;threadid<threadsize;threadid++){
new DownloadThread(threadid,block,url,file).start();
}
}else {
System.out.println("下载失败!!!");
}
}
//创建线程类
private class DownloadThread extends Thread{
private int threadid;
private int block;
private URL url;
private File file;
public DownloadThread(int threadid, int block, URL url, File file)
{
this.threadid = threadid;
this.block = block;
this.url = url;
this.file = file;
}
@Override
public void run()
{
 int start=threadid*block;//计算该线程从网络文件的什么位置开始下载
 int end=(threadid+1)*block-1;//下载到网络文件的什么位置结束
 RandomAccessFile accessFile;
try
{
accessFile = new RandomAccessFile(file, "rwd");
 accessFile.seek(start);
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes="+start+"-"+end);
//指定下载的区域是个范围byte=0-1000
//注意这里的conn.getResponseCode()==200应该是206,206才对,不然会报错
//if(conn.getResponseCode()==200){
InputStream inpuStream=conn.getInputStream();
byte[] buffer=new byte[1024];
int len=0;
while((len=inpuStream.read(buffer))!=-1){
accessFile.write(buffer,0,len);
}
accessFile.close();
inpuStream.close();
//}
System.out.println("第"+(threadid+1)+"线程已经下载完了");
} catch (Exception e)
{
e.printStackTrace();
}
 
}
}
//得到要创建的文件名,根据路径
private String getFileName(String path)
{
 
return path.substring(path.lastIndexOf("/")+1);
}
}
------------------------------------------------------------------------------
2.下面这个是别人提供的,也可以实现下载,原理差不多
  /DownloadTest/src/com/credream/downloadFile/FileDownLoader.java
  package com.credream.downloadFile;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import org.junit.Test;
public class FileDownLoader {
@Test
public void download() throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "image/gif, image/jpeg, 
image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, 
application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, 
application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; 
MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 
3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Connection", "Keep-Alive");
System.out.println(conn.getResponseCode());
int filesize = conn.getContentLength();//得到文件大小
conn.disconnect();
int threasize = 3;//线程数
int perthreadsize = filesize / 3 + 1;
RandomAccessFile file = new RandomAccessFile("102.wma","rw");
file.setLength(filesize);//设置本地文件的大小
file.close();
for(int i=0; i<threasize ; i++){
int startpos = i * perthreadsize;//计算每条线程的下载位置
RandomAccessFile perthreadfile = new RandomAccessFile
("102.wma","rw");//
perthreadfile.seek(startpos);//从文件的什么位置开始写入数
new DownladerThread(i, path, startpos, perthreadsize, 
perthreadfile).start();
}
//以下代码要求用户输入q才会退出测试方法,如果没有下面代码,会因为
进程结束而导致进程内的下载线程被销毁
int quit = System.in.read();
while('q'!=quit){
Thread.sleep(2 * 1000);
}
}
private class DownladerThread extends Thread{
private int startpos;//从文件的什么位置开始下载
private int perthreadsize;//每条线程需要下载的文件大小
private String path;
private RandomAccessFile file;
private int threadid;
public DownladerThread(int threadid, String path, int startpos, 
int perthreadsize, RandomAccessFile perthreadfile) {
this.path = path;
this.startpos = startpos;
this.perthreadsize = perthreadsize;
this.file = perthreadfile;
this.threadid = threadid;
}
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection)
url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "image/gif, image/jpeg, 
image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, 
application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, 
application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Range", "bytes=" + this.startpos 
+ "-");
InputStream inStream = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
int length = 0;
while(length<perthreadsize && (len = inStream.read
(buffer))!=-1){
file.write(buffer, 0, len);
length += len;//累计该线程下载的总大小
}
file.close();
inStream.close();
System.out.println(threadid+ "线程完成下载");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

抱歉!评论已关闭.