单线程下载:
1.定义网络资源地址
2.打开连接
3.获取输入流,定义输出流
4.下载文件
好处:不会对服务器造成压力
弊端:当有多个进程时,CPU不会分配资源给其他线程,处理时间会变长。
多线程下载:
跟单线程下载逻辑是相同的,主要在第三部有改进,将线程分块进行下载以此提高效率。
ps:若文件过大,多线程下载会创建N个线程,容易耗尽内存资源,所以代码中设置了下载数量,
若下载到达5个线程,不再新增下载线程,等到下载子线程下载完成,在创建新的下载线程
package com.yc.demo.multidown;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
import com.yc.utils.CloseUtils;
public class Download {
private int downSize=1024*1024;
private String urlPath;
private String filePath;
//定义下载任务的计数器
private int threadCount=0;
public Download(String urlPath, String filePath) {
this.urlPath = urlPath;
this.filePath = filePath;
}
//单线程下载
public void SingleDownload() throws IOException{
InputStream in=null;
FileOutputStream out=null;
try{
//定义网络资源地址
URL url=new URL(urlPath);
//打开连接
URLConnection con=url.openConnection();
//获取输入流
in=con.getInputStream();
//截取文件名()
String fileName=urlPath.substring(urlPath.lastIndexOf("/")+1);
//定义文件输出流
out=new FileOutputStream(filePath+"/"+fileName);
//下载文件
byte[] b=new byte[1024];
int count;
int countSum=0;
int rate=0;
int filesize=con.getContentLength(); //文件长度
while((count=in.read(b))>0){
countSum+=count;
int curRate=(int)( ( (float)countSum/(float)filesize ) *100 );
if(curRate>rate){
rate=curRate;
System.out.println("文件下载进度:"+rate+"%");
}
out.write(b, 0, count);
}
System.out.println("下载完毕");
}finally{
in.close();
out.close();
}
}
//多线程下载
public void multiDownload() throws IOException{
//定义网络资源地址
URL url=new URL(urlPath);
//打开连接
URLConnection con=url.openConnection();
//截取文件名()
String fileName=urlPath.substring(urlPath.lastIndexOf("/")+1);
int filesize=con.getContentLength(); //定义随机访问文件对象
//计算文件分段的段数
int downLength=filesize/downSize;
downLength+=filesize%downSize==0 ? 0:1;
//定义final变量给匿名类使用
final int fDownLength=downLength;
for(int i=0;i<downLength;i++){
//定义final变量给匿名类使用
final int fi=i;
synchronized (this) {
if(threadCount>5){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
threadCount++; //每创建一个线程计数器+1
}
new Thread(){
//获取输入流
public void run(){
System.out.println("开始分段下载任务:"+fi);
//打开连接
InputStream subIn=null;
RandomAccessFile subOut=null;
try {
URLConnection subCon = url.openConnection();
subIn=subCon.getInputStream();
//定义随机访问文件对象
subOut=new RandomAccessFile(filePath+"/"+fileName,"rw");
//计算下载的起始位置
int beginPos=fi*downSize;
int endPos;
//最后一段,并且未整除
if(fi==fDownLength-1 && filesize%downSize>0){
endPos=beginPos+filesize%downSize;
}else{
endPos=beginPos+downSize;
}
//输入流和输出流同时跳过beginPos个字节
subIn.skip(beginPos);
subOut.seek(beginPos);
//统计下载的数量
int curPos=beginPos;
//下载文件
byte[] b=new byte[1024];
while(true){
if(curPos<endPos){
int count=0;
if(endPos-curPos<1024){
count=subIn.read(b, 0, endPos-curPos);
}else{
count=subIn.read(b);
}
if(count>0){
subOut.write(b, 0, count);
curPos+=count;
}else{
break;
}
}else{
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
CloseUtils.close(subIn,subOut);
}
System.out.println("分段下载任务完成:"+fi);
//通知Download.this继续进行新的线程下载
synchronized (Download.this) {
Download.this.notify();
Download.this.threadCount--;
}
}
}.start();
}
//System.out.println("下载完毕");
}
/**
* 单线程下载,使用main方法实现
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Download down=new Download("http://data.5sing.kgimg.com/G131/M09/03/0D/ww0DAFr3nF-AG3w-AJXnKJqip8Y361.mp3","d:/aaa/");
down.multiDownload();
}
}