多线程下载器
1 获取文件下载地址
package com.xie;
import java.util.Scanner;
//主类
public class Main {
public static void main(String[] args) {
//下载地址
String url=null;
if(args ==null || args.length==0){
for (; ;){
System.out.println("请输入下载地址");
Scanner scanner = new Scanner(System.in);
url= scanner.next();
if (url!=null ){
break;
}
}
}else {
url = args[0];
}
System.out.println("下载地址为:"+url);
}
}
2 文件下载器的基本代码
package com.xie.util;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 获取HttpURLConnection链接对象
* url文件地址
* */
public class HttpUtils {
public static HttpURLConnection getHttpConnection(String url) throws IOException {
URL httpUrl = new URL(url);
// 打开URL连接并强制转换为HttpURLConnection类型
HttpURLConnection httpUrlConnection =(HttpURLConnection)httpUrl.openConnection();
//向文件所在服务器发送标识信息
// 设置请求头User-Agent,模拟浏览器访问(避免被某些服务器拒绝)
// 这里使用的是Edge浏览器的User-Agent标识
httpUrlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763");
return httpUrlConnection;
}
//获取下载文件的文件名
public static String getHttoFileNames(String url) {
int index=url.lastIndexOf("/");// 查找URL中最后一个"/"的位置
return url.substring(index+1); // 截取从最后一个"/"后面开始到URL末尾的子字符串(即文件名)
}
}
3 文件下载器的基本功能
注意:无法直接在C:\
根目录下创建文件,若是硬要在下面创建文件,应当以管理员的身份运行idea
package com.xie.core;
import com.xie.constant.Constant;
import com.xie.util.HttpUtils;
import java.io.*;
import java.net.HttpURLConnection;
//下载器
public class Downloader {
public static void download(String url) {
//获取文件名
String httpFileNames = HttpUtils.getHttoFileNames(url);
//文件下载路径
httpFileNames = Constant.PATH + httpFileNames;
//获取链接对象
HttpURLConnection httpConnection=null;
try {
httpConnection = HttpUtils.getHttpConnection(url);
} catch (IOException e) {
throw new RuntimeException(e);
}
//拿下载文件的输入流,及对其异常进行处理
try(
InputStream input=httpConnection.getInputStream();//从 HTTP 连接获取的输入流(用于读取远程文件)
BufferedInputStream bis=new BufferedInputStream(input);//缓冲输入流(提高读取效率)。
FileOutputStream fos=new FileOutputStream(httpFileNames);//文件输出流(用于写入本地文件)。
BufferedOutputStream bos=new BufferedOutputStream(fos);//缓冲输出流(提高写入效率)
){
int len=-1;
while((len=bis.read())!=-1){
bos.write(len);
}
}catch (FileNotFoundException e){
System.out.println(e.getMessage());
}catch (IOException e){
System.out.println("下载失败");
}finally {
//关闭连接对象
if(httpConnection!=null){
httpConnection.disconnect();
}
}
}
}
4 日志工具类的编写
package com.xie.LogUtil;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
//日志工具类
public class LogUtil {
public static void info(String msg , Object... args){
print(msg,"-info-",args);
}
public static void error(String msg , Object... args){
print(msg,"-error-",args);
}
private static void print(String msg , String level , Object... args){
if(args.length > 0&&args != null){
msg = String.format(msg.replace("{}","%s"), args);
//将格式字符串中的%s替换为args内容。%s是格式说明符。
}
Thread name= Thread.currentThread();
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+" "+name+level+msg);
}
}
5 ScheduledExecutorService的简介
5.1 延迟单次执行schedule()
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
-
作用:延迟一定时间后执行任务(仅执行一次)。
-
参数:
command
:要执行的任务(Runnable
)。delay
:延迟时间。unit
:时间单位(如TimeUnit.SECONDS
)。
示例代码:
//获取对象 ScheduledExecutorService s = Executors.newScheduledThreadPool(1); //延迟两秒后执行任务 s.schedule(()->System.out.println(Thread.currentThread().getName()), 1, TimeUnit.SECONDS); //关闭 s.shutdown();
5.2 周期性执行(固定延迟)scheduleAtFixedRate()
ScheduledFuture<?> scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit )
-
作用:以固定的 时间间隔 执行任务(从任务 开始时间 计算间隔)。
-
特点:
- 如果任务执行时间超过
period
,下次任务会 立即开始(无重叠延迟)。
示例代码:
//获取对象 ScheduledExecutorService s = Executors.newScheduledThreadPool(1); s.scheduleAtFixedRate(()->{ System.out.println(System.currentTimeMillis());; //模拟耗时操作 try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { throw new RuntimeException(e); } },2,3,TimeUnit.SECONDS); //任务会延迟2秒之后再执行,执行完后会等待3秒继续执行, // 但若是任务执行时间大于period的时间则之后不会再等待period的时间而是继续执行该任务
5.3 周期性执行(固定间隔)scheduleWithFixedDelay()
ScheduledFuture<?> scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit )
-
作用:以固定的 任务完成后的延迟 执行下一次任务。
-
特点:
- 无论任务执行多久,两次任务之间始终间隔
delay
。
示例代码:
//获取对象 ScheduledExecutorService s = Executors.newScheduledThreadPool(1); s.scheduleWithFixedDelay(()->{ System.out.println(System.currentTimeMillis());; //模拟耗时操作 try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { throw new RuntimeException(e); } },2,3,TimeUnit.SECONDS); //任务会延迟2秒之后再执行,执行完后会等待3秒继续执行, // 但若是任务执行时间大于period的时间则之后会再等待period的时间而是继续执行该任务
6 编写下载信息线程类
ackage com.xie.core; import com.xie.constant.Constant; public class DownloadInfoThread implements Runnable { //下载的文件总大小,给他赋值的单位是字节 private long httpFileContentLength; //本地已下载文件的大小 public double downSize; //前一次下载的大小 public double preSize; public DownloadInfoThread(long httpFileContentLength) { this.httpFileContentLength = httpFileContentLength; } @Override public void run() { //计算文件总大小 单位:mb String httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB); //计算每秒下载速度 kb int speed=(int)((downSize - preSize)/1024d); preSize=downSize; //剩余文件大小 double remainSize=httpFileContentLength-downSize; //计算剩余时间 String remainTime=String.format(".1f",remainSize=remainSize/1024d/speed); if ("Infinity".equals(remainTime)) { remainTime="-"; } //已下载大小 String currentFileSize= String.format("%.2f",downSize/Constant.MB); String downInfo=String.format("已下载%smb,速度 %skb/s,剩余时间 %ss", currentFileSize,httpFileSize,remainTime); System.out.print("\r"); System.out.print(downInfo); } }
7 文件切割功能的实现
7.1 分块下载工具方法的实现
public static HttpURLConnection getHttpURLConnection(String url,long startPos,long endPos) throws IOException { HttpURLConnection httpURLConnection = getHttpUrlConnection(url); httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos); if (endPos!=0){ httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos); }else { httpURLConnection.setRequestProperty("Range","bytes=" + startPos); } return httpURLConnection; }
- 无论任务执行多久,两次任务之间始终间隔
- 如果任务执行时间超过
8 分块任务类
package com.xie.core;
import com.xie.constant.Constant;
import com.xie.util.HttpUtils;
import com.xie.util.LogUtil;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.concurrent.Callable;
//分块下载任务
public class DownloaderTask implements Callable<Boolean> {
private String url;
private long startPos;
private long endPos;
//标识是哪一部分
private int part;
public DownloaderTask(String url, long startPos, long endPos, int part) {
this.url = url;
this.startPos = startPos;
this.endPos = endPos;
this.part = part;
}
@Override
public Boolean call() throws Exception {
//获取文件名
String httpFileNames = HttpUtils.getHttoFileNames(url);
//设置分块的文件名
httpFileNames =httpFileNames +".temp"+part;
//下载路径
httpFileNames= Constant.PATH+httpFileNames;
//获取分块下载的链接
HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);
try(InputStream input=httpURLConnection.getInputStream();
BufferedInputStream bis=new BufferedInputStream(input);
RandomAccessFile accessFile=new RandomAccessFile(httpFileNames,"rw");
){
byte[] buffer = new byte[Constant.BYTE_SIZE];
int length=-1;
while((length=bis.read(buffer))!=-1){
accessFile.write(buffer,0,length);
}
}catch (FileNotFoundException e){
LogUtil.error("下载文件不存在{}",url);
return false;
}catch (Exception e){
LogUtil.error("下载出现异常");
return false;
}finally {
httpURLConnection.disconnect();
}
return true;
}
}
9多线程下载器源码
9.1 constant包下的Constant类
package com.xie.constant;
//常量类
public class Constant {
public static final String PATH="C:\\";
public static final double MB=1024*1024;
public static final int BYTE_SIZE=1024*100;
}
9.2 core包下的类
9.2.1 Downloader类
package com.xie.core;
import com.xie.util.LogUtil;
import com.xie.constant.Constant;
import com.xie.util.FileUtils;
import com.xie.util.HttpUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
//下载器
public class Downloader {
public ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void download(String url) {
//获取文件名
String httpFileNames = HttpUtils.getHttoFileNames(url);
//文件下载路径
httpFileNames = Constant.PATH + httpFileNames;
//获取本地文件的大小
long locaFileLength= FileUtils.getFileContentLength(httpFileNames);
//获取链接对象
HttpURLConnection httpConnection=null;
DownloadInfoThread downloadInfoThread =null;
try {
httpConnection = HttpUtils.getHttpUrlConnection(url);
//获取下载文件总大小
int contentLength = httpConnection.getContentLength();
//判断文件是否已经下载过
if (locaFileLength>=contentLength) {
LogUtil.info("{}已下载完毕,无需重新下载", httpFileNames);
}
//创建获取下载信息的任务对象
downloadInfoThread = new DownloadInfoThread();
//将任务交给线程执行,每隔一秒执行一次
scheduler.scheduleAtFixedRate(downloadInfoThread,1,1, TimeUnit.MINUTES);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
System.out.println("\r");
System.out.println("下载完成");
//关闭连接对象
if(httpConnection!=null){
httpConnection.disconnect();
}
//关闭
scheduler.shutdownNow();
}
}
}
9.2.2 DownloaderTask
package com.xie.core;
import com.xie.constant.Constant;
import com.xie.util.HttpUtils;
import com.xie.util.LogUtil;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.concurrent.Callable;
//分块下载任务
public class DownloaderTask implements Callable<Boolean> {
private String url;
private long startPos;
private long endPos;
//标识是哪一部分
private int part;
public DownloaderTask(String url, long startPos, long endPos, int part) {
this.url = url;
this.startPos = startPos;
this.endPos = endPos;
this.part = part;
}
@Override
public Boolean call() throws Exception {
//获取文件名
String httpFileNames = HttpUtils.getHttoFileNames(url);
//设置分块的文件名
httpFileNames =httpFileNames +".temp"+part;
//下载路径
httpFileNames= Constant.PATH+httpFileNames;
//获取分块下载的链接
HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);
try(InputStream input=httpURLConnection.getInputStream();
BufferedInputStream bis=new BufferedInputStream(input);
RandomAccessFile accessFile=new RandomAccessFile(httpFileNames,"rw");
){
byte[] buffer = new byte[Constant.BYTE_SIZE];
int length=-1;
while((length=bis.read(buffer))!=-1){
accessFile.write(buffer,0,length);
}
}catch (FileNotFoundException e){
LogUtil.error("下载文件不存在{}",url);
return false;
}catch (Exception e){
LogUtil.error("下载出现异常");
return false;
}finally {
httpURLConnection.disconnect();
}
return true;
}
}
9.2.3 DownloadInfoThread
package com.xie.core;
import com.xie.constant.Constant;
public class DownloadInfoThread implements Runnable {
//下载的文件总大小,给他赋值的单位是字节
private long httpFileContentLength;
//本地已下载文件的大小
public double downSize;
//前一次下载的大小
public double preSize;
public DownloadInfoThread() {
this.httpFileContentLength = httpFileContentLength;
}
@Override
public void run() {
//计算文件总大小 单位:mb
String httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB);
//计算每秒下载速度 kb
int speed=(int)((downSize - preSize)/1024d);
preSize=downSize;
//剩余文件大小
double remainSize=httpFileContentLength-downSize;
//计算剩余时间
String remainTime=String.format(".1f",remainSize=remainSize/1024d/speed);
if ("Infinity".equals(remainTime)) {
remainTime="-";
}
//已下载大小
String currentFileSize= String.format("%.2f",downSize/Constant.MB);
String downInfo=String.format("已下载%smb,速度 %skb/s,剩余时间 %ss",
currentFileSize,httpFileSize,remainTime);
System.out.print("\r");
System.out.print(downInfo);
}
}
9.3 util包下的类
9.3.1 FileUtils
package com.xie.util;
import java.io.File;
public class FileUtils {
//获取本地文件的大小
public static long getFileContentLength(String path){
File file = new File(path);
return file.exists() && file.isFile() ? file.length() : 0;
}
}
9.3.2 HttpUtils
package com.xie.util;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* 获取HttpURLConnection链接对象
* url文件地址
* */
public class HttpUtils {
public static HttpURLConnection getHttpUrlConnection(String url) throws IOException {
URL httpUrl = new URL(url);
HttpURLConnection httpUrlConnection =(HttpURLConnection)httpUrl.openConnection();
//向文件所在服务器发送标识信息
httpUrlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763");
return httpUrlConnection;
}
//获取下载文件的文件名
public static String getHttoFileNames(String url) {
int index=url.lastIndexOf("/");
return url.substring(index+1);
}
public static HttpURLConnection getHttpURLConnection(String url,long startPos,long endPos) throws IOException {
HttpURLConnection httpURLConnection = getHttpUrlConnection(url);
httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos);
if (endPos!=0){
httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos);
}else {
httpURLConnection.setRequestProperty("Range","bytes=" + startPos);
}
return httpURLConnection;
}
}
9.3.3
package com.xie.util;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
//日志工具类
public class LogUtil {
public static void info(String msg , Object... args){
print(msg,"-info-",args);
}
public static void error(String msg , Object... args){
print(msg,"-error-",args);
}
private static void print(String msg , String level , Object... args){
if(args.length > 0&&args != null){
msg = String.format(msg.replace("{}","%s"), args);
//将格式字符串中的%s替换为args内容。%s是格式说明符。
}
Thread name= Thread.currentThread();
System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+" "+name+level+msg);
}
}
9.4 主类
package com.xie;
import com.xie.util.LogUtil;
import com.xie.core.Downloader;
import java.util.Scanner;
//主类
public class Main {
public static void main(String[] args) {
//下载地址
String url=null;
if(args ==null || args.length==0){
for (; ;){
LogUtil.info("请输入下载地址");
Scanner scanner = new Scanner(System.in);
url= scanner.next();
if (url!=null ){
break;
}
}
}else {
url = args[0];
}
Downloader downloader = new Downloader();
downloader.download(url);
}
}
```java
package com.xie;
import com.xie.util.LogUtil;
import com.xie.core.Downloader;
import java.util.Scanner;
//主类
public class Main {
public static void main(String[] args) {
//下载地址
String url=null;
if(args ==null || args.length==0){
for (; ;){
LogUtil.info("请输入下载地址");
Scanner scanner = new Scanner(System.in);
url= scanner.next();
if (url!=null ){
break;
}
}
}else {
url = args[0];
}
Downloader downloader = new Downloader();
downloader.download(url);
}
}