(1-6-5)Java 多线程(定长线程池)下载器(案例)
目录
0.案例需求
1. 封装URLConnection 网络下载工具类
2. 读取 config.properties 配置文件
3. 从指定文件读取下载地址,批量下载网络资源
4. 实现多线程(定长线程池)下载 及 本地文件保存
5. 完整版源码
0.案例需求
- 通过读取源文件,多线程自动下载所有网络资源到 本地硬盘
- 遇到下载故障时,在控制台打印错误信息
- 允许自定义源文件地址,保存下载文件的目录要自动创建
- 允许自定义同时下载的任务数量,不指定时,默认开启3个下载任务
- 下载成功后,在控制太输出存储路径与文件尺寸
代码实现思路
实现效果:
1. 封装URLConnection 网络下载工具类
/*** 下载单个文件 保存到本地* @param url 原图片网址* @param targetDir 保存的目标目录*/
// 1. 封装URLConnection 网络下载工具类
public class Downloader {/*** 下载单个文件 保存到本地* @param url 原图片网址* @param targetDir 保存的目标目录*/public int download(String url, String targetDir, int index) {InputStream is = null;OutputStream os = null;try{/*** 由于网络图片不以 文件名结尾* 故采用 id+1 重新命名*/
// String fileName = url.substring(url.lastIndexOf("/") + 1);String fileName = "摩托姐" + index + ".png";File targetFile = new File(targetDir + "/" +fileName);if(!targetFile.exists()){targetFile.createNewFile();}URL urlObj = new URL(url);URLConnection connection = urlObj.openConnection();is = connection.getInputStream();os = new FileOutputStream(targetFile);byte[] buffer = new byte[1024];int len = 0;while((len = is.read(buffer)) != -1){os.write(buffer, 0, len);}System.out.println("[info]图片下载完毕:" + url + "\n\t ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");}catch(IOException e){e.printStackTrace();}finally{try{if(os != null){os.close();}if(is != null){is.close();}}catch(IOException e){e.printStackTrace();}}return index++;}public static void main(String[] args) {int index = 1;Downloader downloader = new Downloader();index = downloader.download("https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667", "D:/game/游戏/王者荣耀/摩托姐", index);}/*** [info]图片下载完毕:https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667* ->D:\game\游戏\王者荣耀\摩托姐\摩托姐1.png(45.0kb)*/
}
2. 读取 config.properties 配置文件
config.properties
thread-num = 3
#中文路径访问不到
#target-dir = D:/game/游戏/王者荣耀/摩托姐
target-dir = D:/game/wzry/mtj
获取config.properties资源中的属性信息
public String start(String propDir){System.out.println("propDir:" + propDir);File propFile = new File(propDir + "/config.properties");Properties prop = new Properties();Reader reader = null;String targetDir = "";try{reader = new FileReader(propFile);prop.load(reader);String threadNum = prop.getProperty("thread-num");targetDir = prop.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();}}}return targetDir;}public static void main(String[] args) {int index = 2;Downloader2 downloader2 = new Downloader2();String path = downloader2.start("D:/s_java/se/6-5moreThreadDownloader/downloader/src");index = downloader2.download("https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667", path, index);}
3. 从指定文件读取下载地址,批量下载网络资源
download.txt
https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667
https://img0.baidu.com/it/u=4153860329,1779549628&fm=253&app=138&f=JPEG?w=1570&h=800
https://img1.baidu.com/it/u=1390259360,3605622436&fm=253&app=138&f=JPEG?w=1534&h=800
https://img2.baidu.com/it/u=799013572,638024149&fm=253&app=138&f=JPEG?w=1570&h=800
https://img2.baidu.com/it/u=1702949672,1328662943&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1778
https://img0.baidu.com/it/u=2015490030,1225562827&fm=253&app=138&f=JPEG?w=800&h=1778
https://images.pianwan.com/img/h1/h231/img202206241604390_info586X364.png
Downloader3.java
package com.vb.loader;import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;// 3. 读取 download.txt 下载列表
public class Downloader3 {public void download(String url, String targetDir, int index) {InputStream is = null;OutputStream os = null;try{
// String fileName = url.substring(url.lastIndexOf("/") + 1);String fileName = "摩托姐" + index + ".png";File targetFile = new File(targetDir + "/" +fileName);if(!targetFile.exists()){targetFile.createNewFile();}URL urlObj = new URL(url);URLConnection connection = urlObj.openConnection();is = connection.getInputStream();os = new FileOutputStream(targetFile);byte[] buffer = new byte[1024];int len = 0;while((len = is.read(buffer)) != -1){os.write(buffer, 0, len);}System.out.println("[info]图片下载完毕:" + url + "\n\t ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");}catch(IOException e){e.printStackTrace();}finally{try{if(os != null){os.close();}if(is != null){is.close();}}catch(IOException e){e.printStackTrace();}}}public void start(String propDir){System.out.println("propDir:" + propDir);File propFile = new File(propDir + "/config.properties");Properties prop = new Properties();Reader reader = null;String targetDir = "";try{reader = new FileReader(propFile);prop.load(reader);String threadNum = prop.getProperty("thread-num");targetDir = prop.getProperty("target-dir");// System.out.println(threadNum);
// System.out.println(targetDir);this.multiDownloaderFile(targetDir, propDir + "/download.txt");}catch (IOException e){e.printStackTrace();}finally{if(reader != null){try {reader.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 从指定文件读取下载地址,批量下载网络资源* @param targetDir 下载后文件的存放目录* @param downloadTxtPath download文件的路径*/public void multiDownloaderFile(String targetDir, String downloadTxtPath){File targetFile = new File(targetDir);if(!targetFile.exists()){targetFile.mkdirs();System.out.println("自动创建目录:" + targetDir);}System.out.println("目标目录:"+ targetDir);List<String> resPaths = new ArrayList<String>();BufferedReader reader = null;try{int flag = 1;reader = new BufferedReader(new FileReader(downloadTxtPath));String line = null;while((line = reader.readLine()) != null){resPaths.add(line);
// System.out.println(line);this.download(line, targetDir, flag);flag++;}}catch(IOException e){e.printStackTrace();}finally{if(reader != null){try{reader.close();}catch(IOException e){e.printStackTrace();}}}}public static void main(String[] args) {
// int index = 1;Downloader3 downloader2 = new Downloader3();downloader2.start("D:/s_java/se/6-5moreThreadDownloader/downloader/src");
// String path = downloader2.start("D:/s_java/se/6-5moreThreadDownloader/downloader/src");
// index = downloader2.download("https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667", path, index);}/*** propDir:D:/s_java/se/6-5moreThreadDownloader/downloader/src* 目标目录:D:/game/wzry/mtj* [info]图片下载完毕:https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667* ->D:\game\wzry\mtj\摩托姐1.png(45.0kb)* [info]图片下载完毕:https://img0.baidu.com/it/u=4153860329,1779549628&fm=253&app=138&f=JPEG?w=1570&h=800* ->D:\game\wzry\mtj\摩托姐2.png(141.0kb)* [info]图片下载完毕:https://img1.baidu.com/it/u=1390259360,3605622436&fm=253&app=138&f=JPEG?w=1534&h=800* ->D:\game\wzry\mtj\摩托姐3.png(105.0kb)* [info]图片下载完毕:https://img2.baidu.com/it/u=799013572,638024149&fm=253&app=138&f=JPEG?w=1570&h=800* ->D:\game\wzry\mtj\摩托姐4.png(133.0kb)* [info]图片下载完毕:https://img2.baidu.com/it/u=1702949672,1328662943&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1778* ->D:\game\wzry\mtj\摩托姐5.png(110.0kb)* [info]图片下载完毕:https://img0.baidu.com/it/u=2015490030,1225562827&fm=253&app=138&f=JPEG?w=800&h=1778* ->D:\game\wzry\mtj\摩托姐6.png(177.0kb)* [info]图片下载完毕:https://images.pianwan.com/img/h1/h231/img202206241604390_info586X364.png* ->D:\game\wzry\mtj\摩托姐7.png(362.0kb)*/
}
4. 实现多线程(定长线程池)下载 及 本地文件保存
// 4. 实现多线程下载 及 本地文件保存
public class Downloader4 {private Integer threadNum = 2;public void download(String url, String targetDir, int index) {InputStream is = null;OutputStream os = null;try{
// String fileName = url.substring(url.lastIndexOf("/") + 1);String fileName = "摩托姐" + index + ".png";File targetFile = new File(targetDir + "/" +fileName);if(!targetFile.exists()){targetFile.createNewFile();}URL urlObj = new URL(url);URLConnection connection = urlObj.openConnection();is = connection.getInputStream();os = new FileOutputStream(targetFile);byte[] buffer = new byte[1024];int len = 0;while((len = is.read(buffer)) != -1){os.write(buffer, 0, len);}
// System.out.println("[info]图片下载完毕:" + url + "\n\t ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");}catch(IOException e){e.printStackTrace();}finally{try{if(os != null){os.close();}if(is != null){is.close();}}catch(IOException e){e.printStackTrace();}}}public void start(String propDir){System.out.println("propDir:" + propDir);File propFile = new File(propDir + "/config.properties");Properties prop = new Properties();Reader reader = null;String targetDir = "";try{reader = new FileReader(propFile);prop.load(reader);String threadNum = prop.getProperty("thread-num");this.threadNum = Integer.parseInt(threadNum);targetDir = prop.getProperty("target-dir");this.multiDownloaderFile(targetDir, propDir + "/download.txt");}catch (IOException e){e.printStackTrace();}finally{if(reader != null){try {reader.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 从指定文件读取下载地址,批量下载网络资源* @param targetDir 下载后文件的存放目录* @param downloadTxtPath download文件的路径*/public void multiDownloaderFile(String targetDir, String downloadTxtPath){File targetFile = new File(targetDir);if(!targetFile.exists()){targetFile.mkdirs();System.out.println("自动创建目录:" + targetDir);}System.out.println("目标目录:"+ targetDir);List<String> resPaths = new ArrayList<String>();BufferedReader reader = null;ExecutorService threadPool = null;int flag = 1;try{reader = new BufferedReader(new FileReader(downloadTxtPath));String line = null;while((line = reader.readLine()) != null){resPaths.add(line);
// System.out.println(line);// 单线程处理
// this.download(line, targetDir, flag);
// flag++;}// 创建定长线程池threadPool = Executors.newFixedThreadPool(this.threadNum);Downloader4 that = this;for(int i = 1; i <= resPaths.size(); i++){int finalI = i;threadPool.execute(new Runnable() {@Overridepublic void run() {that.download(resPaths.get(finalI -1), targetDir, finalI);System.out.println("[info]" +Thread.currentThread().getName()+",图片下载完毕:" + resPaths.get(finalI -1) + "\n\t ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");}});}}catch(IOException e){e.printStackTrace();}finally{if(threadPool != null){threadPool.shutdown();}if(reader != null){try{reader.close();}catch(IOException e){e.printStackTrace();}}}}public static void main(String[] args) {
// int index = 1;Downloader4 downloader2 = new Downloader4();downloader2.start("D:/s_java/se/6-5moreThreadDownloader/downloader/src");
// String path = downloader2.start("D:/s_java/se/6-5moreThreadDownloader/downloader/src");
// index = downloader2.download("https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667", path, index);}/*** 目标目录:D:/game/wzry/mtj* [info]pool-1-thread-1,图片下载完毕:https://img2.baidu.com/it/u=1567841056,1924556786&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667* ->D:\game\wzry\mtj(4.0kb)* [info]pool-1-thread-3,图片下载完毕:https://img1.baidu.com/it/u=1390259360,3605622436&fm=253&app=138&f=JPEG?w=1534&h=800* ->D:\game\wzry\mtj(4.0kb)* [info]pool-1-thread-2,图片下载完毕:https://img0.baidu.com/it/u=4153860329,1779549628&fm=253&app=138&f=JPEG?w=1570&h=800* ->D:\game\wzry\mtj(4.0kb)* [info]pool-1-thread-1,图片下载完毕:https://img2.baidu.com/it/u=799013572,638024149&fm=253&app=138&f=JPEG?w=1570&h=800* ->D:\game\wzry\mtj(4.0kb)* [info]pool-1-thread-3,图片下载完毕:https://img2.baidu.com/it/u=1702949672,1328662943&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1778* ->D:\game\wzry\mtj(4.0kb)* [info]pool-1-thread-2,图片下载完毕:https://img0.baidu.com/it/u=2015490030,1225562827&fm=253&app=138&f=JPEG?w=800&h=1778* ->D:\game\wzry\mtj(4.0kb)* [info]pool-1-thread-1,图片下载完毕:https://images.pianwan.com/img/h1/h231/img202206241604390_info586X364.png* ->D:\game\wzry\mtj(4.0kb)*/
}
5. 完整版源码
package com.vb.loader;import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class DownloaderDemo {private Integer threadNum = 2;public void download(String url, String targetDir, int index) {InputStream is = null;OutputStream os = null;try{String fileName = "摩托姐" + index + ".png";File targetFile = new File(targetDir + "/" +fileName);if(!targetFile.exists()){targetFile.createNewFile();}URL urlObj = new URL(url);URLConnection connection = urlObj.openConnection();is = connection.getInputStream();os = new FileOutputStream(targetFile);byte[] buffer = new byte[1024];int len = 0;while((len = is.read(buffer)) != -1){os.write(buffer, 0, len);}}catch(IOException e){e.printStackTrace();}finally{try{if(os != null){os.close();}if(is != null){is.close();}}catch(IOException e){e.printStackTrace();}}}public void start(String propDir){System.out.println("propDir:" + propDir);File propFile = new File(propDir + "/config.properties");Properties prop = new Properties();Reader reader = null;String targetDir = "";try{reader = new FileReader(propFile);prop.load(reader);String threadNum = prop.getProperty("thread-num");this.threadNum = Integer.parseInt(threadNum);targetDir = prop.getProperty("target-dir");this.multiDownloaderFile(targetDir, propDir + "/download.txt");}catch (IOException e){e.printStackTrace();}finally{if(reader != null){try {reader.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 从指定文件读取下载地址,批量下载网络资源* @param targetDir 下载后文件的存放目录* @param downloadTxtPath download文件的路径*/public void multiDownloaderFile(String targetDir, String downloadTxtPath){File targetFile = new File(targetDir);if(!targetFile.exists()){targetFile.mkdirs();System.out.println("自动创建目录:" + targetDir);}System.out.println("目标目录:"+ targetDir);List<String> resPaths = new ArrayList<String>();BufferedReader reader = null;ExecutorService threadPool = null;int flag = 1;try{reader = new BufferedReader(new FileReader(downloadTxtPath));String line = null;while((line = reader.readLine()) != null){resPaths.add(line);}// 创建定长线程池threadPool = Executors.newFixedThreadPool(this.threadNum);DownloaderDemo that = this;for(int i = 1; i <= resPaths.size(); i++){int finalI = i;threadPool.execute(new Runnable() {@Overridepublic void run() {that.download(resPaths.get(finalI -1), targetDir, finalI);System.out.println("[info]" +Thread.currentThread().getName()+",图片下载完毕:" + resPaths.get(finalI -1) + "\n\t ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");}});}}catch(IOException e){e.printStackTrace();}finally{if(threadPool != null){threadPool.shutdown();}if(reader != null){try{reader.close();}catch(IOException e){e.printStackTrace();}}}}public static void main(String[] args) {DownloaderDemo downloader2 = new DownloaderDemo();downloader2.start("D:/s_java/se/6-5moreThreadDownloader/downloader/src");}
}
目录结构
注:其他资源文件可在1~4章节复制