单线程下载:
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(); } }