之前学习了集合、泛型、多线程、I/O四个Java最常用的技术,那这些技术在实际开发中如何使用呢?
以综合案例:多线程下载器,将上述技术串联起来。
也是一个非常实用的小工具。
一、批量下载器
1.1需求分析
借鉴迅雷多线程下载的思路,多个任务并行运行。因此,具体的功能有:
(1)通过读取源文件,多线程自动下载所有网络资源到本地
(2)遇到下载故障,在控制台上打印错误信息
(3)允许自定义源文件的地址,保存下载文件的目录要自动创建
(4)允许自定义同时下载的任务数量,不指定默认开启10个下载任务
(5)下载文件的文件名,要与网址包含文件名保持一致
(6)下载成功后,在控制台输出存储路径与文件尺寸
1.2 实现思路
1.3 成品演示
略。
二、多线程资源下载及异常处理
2.1 封装URLConnection单文件下载器
1.多线程下载器,其实是基于单文件下载器的,然后并行运行。所以,先实现单文件的下载和保存操作:
public class Downloader {
/**
* 下载多个文件保存到本地
* @param source 原图片的网址
* @param targetDir 目标目录,要确保已存在
*/
public void download(String source, String targetDir) {
InputStream is = null; //用于读取来自网络的数据流
OutputStream os = null; //用于向指定文件写入
try {
//示例:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
//注意:是jpeg,不是jpg!
String fileName = source.substring(source.lastIndexOf("/" )+1); //从p开始读取,获取文件名 //1.注意+1的位置
File targetFile = new File(targetDir + "/" + fileName); //定义目标文件
if (!targetFile.exists()) { //当前的目标文件,可能不存在,所以要判断一下
targetFile.createNewFile();
}
URL url = new URL(source); //实例化一个URL对象,将原图片网址的参数传进去
URLConnection connection = url.openConnection();
is = connection.getInputStream(); //得到网络上图片的输入流
os = new FileOutputStream(targetFile); //向上面已创建好的目标文件,进行写入
byte[] bs = new byte[1024]; //通过字节流来读取is对象 //最多读1k字节
int len = 0;
while ((len = is.read(bs)) != -1) { //当网络上图片的输入流is还没读完时
//一边读一边写
os.write(bs, 0, len); //要读取的内容+从哪里开始读+有效的部分
}
//友好: 一般性消息; 换行,缩进, 防止出现小数,向下取整
System.out.println("[INFO]图片下载完毕:"+source+"\n\t ->"+targetFile.getPath()+"("+Math.floor(targetFile.length()/1024)+"kb)");
} catch (IOException e) {
e.printStackTrace();
} finally { //关闭流,也要try-catch进行包裹,因为关闭流也可能会抛出I/O异常
try {
if (os != null) {
os.close(); //关闭os流
}
if (is != null) {
is.close(); //关闭is流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Downloader downloader = new Downloader();
downloader.download("https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg","e:/a/d");
}
}
--->
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
->e:\a\d\pexels-photo-11572548.jpeg(49.0kb)
成功保存到了本地目录中:
2.拓展:创建对象时,构造参数的提示,在哪里设置?每次鼠标到那里就不提示。
(或者可以提升为:方法使用时,如何自动显示参数的提示)
- 问题:
- 解决方案:
勾选上以下两项;
如果参数列表提示几秒钟消失了,可以使用快捷键Ctrl+P显示参数。
2.2 读取config.properties配置文件
1.Properties类
(1)Properties类是java.utils包专用于读取应用属性文件的类
(2)Properties文件存储数据的形式是:属性名=属性值
(3)多线程下载器,用config.properties来保存程序的配置信息
其中,config是文件名,可以改;properties是扩展名格式,不能改。
2.读取config.properties配置文件
以上是写死了目标文件的目录,如果要调整,就要修改源代码,然后重新编译,甚至重新部署和发布,着非常麻烦。
因此,应该将未来可能会灵活调整的配置项,独立出来放到一个文件中。
在src
目录下,新建文件:config.properties
,放一些未来可能会灵活调整的配置项:
在文件config.properties
中:
thread-num=10; //配置文件的书写都是紧密的,不能出现空格
target-dir=e:/a/d
以上是错误的写法,正确的是:
- 不能有空格、空行
- 也不能有分号
thread-num=10
target-dir=e:/a/d
config.properties配置文件在格式上,很娇气~
3.Properties类的使用
public class Downloader {
public void download(String source, String targetDir) {
...
public static void main(String[] args) {
File propFile = new File("E:\\code\\downloader\\src\\config.properties"); //创建一个文件的对象,放入配置文件的路径
Properties properties = new Properties(); //实例化Properties类
Reader reader=null;
try {
reader = new FileReader(propFile);
properties.load(reader); //加载指定的字符输入流,以加载配置文件
String threadNum = properties.getProperty("thread-num"); //读取配置文件的内容(键值对)
String targetDir = properties.getProperty("target-dir");
System.out.println(threadNum); //检查下配置文件获取是否正常
System.out.println(targetDir);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Downloader downloader = new Downloader();
downloader.download("https://manongbiji.oss-cn-beijing.aliyuncs.com/images/weixin.jpg","e:/a/d");
}
}
--->
10;
e:/a/d
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/images/weixin.jpg
->e:\a\d\weixin.jpg(22.0kb)
4.结合工程得到使用
public class Downloader {
...
//开始多线程下载
public void start(String propDir) { //参数:配置文件的目录
File propFile = new File(propDir+"\\config.properties"); //创建一个文件对象,放入配置文件的路径
Properties properties = new Properties();
Reader reader=null;
try {
reader = new FileReader(propFile);
properties.load(reader); //加载指定的字符输入流,以加载配置文件
String threadNum = properties.getProperty("thread-num"); //读取位置文件的内容
String targetDir = properties.getProperty("target-dir");
System.out.println(threadNum); //检查下配置文件获取是否正常
System.out.println(targetDir);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Downloader downloader = new Downloader();
downloader.start("E:\\code\\downloader\\src"); //只需要传入src文件的目录的路径
}
}
--->
10; //成功读取到了config.properties配置文件中的配置项
e:/a/d
5.拓展:
- 错误的信息
- 正确的做法:
应该是独立的实例化,然后将对象声明放在try块外面,用于关闭。
2.3 读取download.txt下载列表
所有的准备从网络上下载资源的网址,都放在了download.txt文本文件中。
下面通过Java I/O的方式,去读取它。
其中,download.txt文本文件必须与上节的配置文件,放在同一级目录下。
- 在
download.txt
中:
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
- 在
Downloader.java
中:
public class Downloader {
/**
* 从指定文件读取下载地址,批量下载网络资源
* @param targetDir 下载文件的存储目录
* @param downloadTxt download.txt完整路径
*/
public void multiDownloadFromFile(String targetDir, String downloadTxt) {
File dir = new File(targetDir); //创建一个文件对象,放入下载文件的存储目录
if (!dir.exists()) { //如果目录不存在,就创建一个
dir.mkdirs();
}
System.out.println("[INFO]发现下载目录[" + dir.getPath() + "]不存在,已自动创建"); //给个友好的提示
//一行一行的读取download.txt中内容、
List<String> resources = new ArrayList(); //定义一个数组集合,用于存放读取来的数据
BufferedReader reader = null; //用缓冲区的Reader对象,进行暂时缓存
try {
reader = new BufferedReader(new FileReader(downloadTxt)); //FileReader的简化使用:套娃形式
String line = null; //代表每一行的文本
while ((line = reader.readLine()) != null) { //判空,如果还有新的一行文本
resources.add(line);
System.out.println(line); //验证下,line是否读取成功
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close(); //上述套娃的使用形式,只关闭最外侧流即可,内侧流会自动关闭
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//开始多线程下载
public void start(String propDir) {
File propFile = new File(propDir+"\\config.properties");
Properties properties = new Properties();
Reader reader=null;
try {
reader = new FileReader(propFile);
properties.load(reader);
String threadNum = properties.getProperty("thread-num");
String targetDir = properties.getProperty("target-dir");
this.multiDownloadFromFile(targetDir, propDir + "\\download.txt"); //调用下面刚定义好的方法
} catch (IOException e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Downloader downloader = new Downloader();
downloader.start("E:\\code\\downloader\\src"); //只需要传入src文件的目录的路径
}
}
--->
[INFO]发现下载目录[e:\a\d]不存在,已自动创建
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg //成功读取
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
2.4 实现多线程下载及本地文件保存
线程池的种类有四种,这里使用的是定长线程池。
将每一个网址,对应一个download的下载任务,来进行并发下载。
public class Downloader {
private Integer threadNum = 10;
/**
* 从指定文件读取下载地址,批量下载网络资源
* @param targetDir 下载文件的存储目录
* @param downloadTxt download.txt完整路径
*/
public void multiDownloadFromFile(String targetDir, String downloadTxt) {
File dir = new File(targetDir);
if (!dir.exists()) {
dir.mkdirs();
}
System.out.println("[INFO]发现下载目录[" + dir.getPath() + "]不存在,已自动创建");
List<String> resources = new ArrayList();
BufferedReader reader = null;
ExecutorService threadPool = null; //声明一个线程池,放在try块外面是为了后续方便关闭
try {
reader = new BufferedReader(new FileReader(downloadTxt));
String line = null;
while ((line = reader.readLine()) != null) {
resources.add(line);
System.out.println(line);
}
threadPool = Executors.newFixedThreadPool(this.threadNum); //创建线程池,定长10个
//用for循环遍历出每个要下载的资源
Downloader that = this; //声明一个外部类的对象,暂时换个名儿
for (String res : resources) { //res:对应每一个网址
threadPool.execute(new Runnable() { //简洁写法,自动创建一个内部的匿名类
@Override
public void run() {
//this.download //this指的是内部的匿名类的对象。因为在不同的类里,this指向的对象是不一样的
that.download(res, targetDir); //并行下载:就是调用download方法
}
});
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (threadPool != null) {
threadPool.shutdown(); //等所有线程处理完毕,就关闭掉线程池 //销毁有10个跑道的小操场
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//开始多线程下载
public void start(String propDir) {
File propFile = new File(propDir+"\\config.properties");
Properties properties = new Properties();
Reader reader=null;
try {
reader = new FileReader(propFile);
properties.load(reader);
String threadNum = properties.getProperty("thread-num");
this.threadNum = Integer.parseInt(threadNum); //将字符串类转换成整型包装类
String targetDir = properties.getProperty("target-dir");
this.multiDownloadFromFile(targetDir, propDir + "\\download.txt");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Downloader downloader = new Downloader();
downloader.start("E:\\code\\downloader\\src");
}
}
--->
[INFO]发现下载目录[e:\a\d]不存在,已自动创建
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
->e:\a\d\pexels-photo-11593467.jpeg(36.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
->e:\a\d\pexels-photo-12240136.jpeg(83.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
->e:\a\d\pexels-photo-12203460.jpeg(90.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
->e:\a\d\pexels-photo-11572548.jpeg(49.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
->e:\a\d\pexels-photo-11631922.jpeg(214.0kb)
成功下载了上述资源:
2.5 更充分的演示程序
- 在
download.txt
中,增加更多的下载资源:
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10092819.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10519162.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10977780.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-11311195.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-11954484.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-12072057.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-12072057.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-4542338.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-6323680.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-6873171.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-7409835.jpeg
- 在文件
config.properties
中,调整配置项:
thread-num=5
target-dir=e:/c/f
- 代码执行结果:
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
->e:\c\f\pexels-photo-11593467.jpeg(36.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
->e:\c\f\pexels-photo-11572548.jpeg(49.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
->e:\c\f\pexels-photo-12240136.jpeg(83.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
->e:\c\f\pexels-photo-12203460.jpeg(90.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10977780.jpeg
->e:\c\f\pexels-photo-10977780.jpeg(62.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
->e:\c\f\pexels-photo-11631922.jpeg(214.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-11954484.jpeg
->e:\c\f\pexels-photo-11954484.jpeg(15.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-12072057.jpeg
->e:\c\f\pexels-photo-12072057.jpeg(41.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10519162.jpeg
->e:\c\f\pexels-photo-10519162.jpeg(13.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-4542338.jpeg
->e:\c\f\pexels-photo-4542338.jpeg(36.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-6323680.jpeg
->e:\c\f\pexels-photo-6323680.jpeg(59.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-6873171.jpeg
->e:\c\f\pexels-photo-6873171.jpeg(44.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10092819.jpeg
->e:\c\f\pexels-photo-10092819.jpeg(32.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-12072057.jpeg
->e:\c\f\pexels-photo-12072057.jpeg(41.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-7409835.jpeg
->e:\c\f\pexels-photo-7409835.jpeg(76.0kb)
[INFO]图片下载完毕:https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-11311195.jpeg
->e:\c\f\pexels-photo-11311195.jpeg(75.0kb)
- 成功下载了上述资源:
Comments | NOTHING