一、需求分析
1.通过读取源文件多线程自动下载所有网络资源到本地硬盘
2.遇到下载故障在控制台打印错误信息
3.允许自定义源文件的地址,保存下载文件的目录要自动创建
4.允许自定义同时下载的任务数量,不指定默认开启10个下载任务
5.下载文件的文件名要与网址包含文件名保持一 致
6.下载成功后在控制台输出存储路径与文件尺寸
二、实现效果
控制台运行效果
通过程序下载的内容
三、实现代码
package com.imooc.downloader;
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 Downloader {
private Integer threadNum = 10;
/**
* 下载单个文件保存到本地
* @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
String fileName = source.substring(source.lastIndexOf("/") + 1);
File targetFile = new File(targetDir + "/" + fileName);
if(!targetFile.exists()){
targetFile.createNewFile();
}
URL url = new URL(source);
URLConnection connection = url.openConnection();
is = connection.getInputStream();
os = new FileOutputStream(targetFile);
byte[] bs = new byte[1024];
int len = 0;
while((len = is.read(bs)) != -1){
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{
if(os != null){
os.close();
}
if(is != null){
is.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
/**
* 从指定文件读取下载地址,批量下载网络资源
* @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存入resources集合
List<String> resources = new ArrayList<>();
BufferedReader reader = null;
ExecutorService threadPool = null;
try {
reader = new BufferedReader(new FileReader(downloadTxt));
String line = null;
//读取每一行网址,存入resources集合
while((line = reader.readLine()) != null){
resources.add(line);
//System.out.println(line);
}
//利用线程池,同时开启{threadNum}个线程批量下载图片并保存到本地
threadPool = Executors.newFixedThreadPool(this.threadNum);
//this在不同的类指向的对象是不一样的,如果要在不同的类内侧使用其他对象的this,可以用that指向别的对象
Downloader that = this;
for(String res:resources){
threadPool.execute(new Runnable() {
@Override
public void run() {
that.download(res,targetDir);
}
});
}
}catch (IOException e){
e.printStackTrace();
}finally {
//执行完毕,关闭线程池
if(threadPool != null){
threadPool.shutdown();
}
if(reader != null){
try {
//关闭字节输入流
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 开始多线程下载
* @param propDir config.properties所在目录
*/
public void start(String propDir){
//指定config.properties完整路径
File propFile = new File(propDir + "\\config.properties");
//实例化Properties属性类
Properties properties = new Properties();
Reader reader = null;
try {
//实例化FileReader对象
reader = new FileReader(propFile);
//properties对象读取配置文件
properties.load(reader);
//通过getProperty方法得到对应的选项值
String threadNum = properties.getProperty("thread-num");
this.threadNum = Integer.parseInt(threadNum);
String targetDir = properties.getProperty("target-dir");
//打印输出
//System.out.println(threadNum);
//System.out.println(targetDir);
//读取download.txt进行多线程下载
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("D:\\code\\downloader\\src");
//downloader.download("https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg","d:/a/d");
}
}
四、目录结构
在src目录下一定要有配置文件config.properties和下载内容的地址download.txt,如下图,两个文件一定要在src同一目录下
配置文件config.properties的内容
一个是线程数量,一个是文件存放地址
thread-num=10
target-dir=d:/a/b
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