Java多线程实现下载三百万头像图片
最近有个要从接口以翻页的形式获取300万数据存入数据库中;并下载头像图片的需求。第一步把接口中数据获取并保存到本地数据库中,第二部从数据库中读取三百万条数据并用多线程下载图片。废话不多说,直接上代码:
从远程服务器获取文件方法:
.
/**
* 从远程服务器获取文件方法
*
* @param urlList 远程服务器中文件全路径(含文件名)
* @param localUrl 本地文件全路径
* @return
* @throws IOException
*/
public static Boolean download(String urlList, String localUrl) throws IOException {
URL url = null;
Boolean b = true;
DataInputStream dataInputStream = null;
FileOutputStream fileOutputStream = null;
try {
url = new URL(urlList);
dataInputStream = new DataInputStream(url.openStream());
//从配置文件中获取服务器ip
String localPath = Config.getStr("localPath");
//判断本地路径是否存在,不存在则创建路径
File file = new File(localPath);
if (!file.exists()) {
file.mkdirs();
}
fileOutputStream = new FileOutputStream(new File(localUrl));
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = dataInputStream.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
fileOutputStream.write(output.toByteArray());
} catch (IOException e) {
e.printStackTrace();
b = false;
} finally {
dataInputStream.close();
fileOutputStream.close();
}
return b;
}
分批获取数据(单批获取10万条数据):
.
/**
* 分批获取数据(单批获取10万条数据)
*
* @return
*/
public static Page<Record> getPhotoDatabase(int pageNumber) {
//启动数据库连接
ConnectionSqlite.conection();
Page<Record> mc = Db.use("photo").paginate(pageNumber, 100000, "select person_id,photo_url,local_photo_url", "from" +
" user_info where result_status=0");
return mc;
}
调接口把三百万条数据读入数据库中后执行该批量下载方法:
.
/**
* 调接口把三百万条数据读入数据库中后执行该批量下载方法
*/
public static void downPng() {
long startTime = System.currentTimeMillis();
//从数据库获取总页数
Page<Record> photoDatabaset = getPhotoDatabase(1);
if (photoDatabaset.getList().size() > 0) {
int totalPage = photoDatabaset.getTotalPage();//总页数
//因为一批下载完之后要修改状态,所以下载页码要倒序下载才能保证完全下载
for (int pageNumber = totalPage; pageNumber > 0; pageNumber--) {
Page<Record> photoDatabase = getPhotoDatabase(pageNumber);
List<Record> records = photoDatabase.getList();
//线程安全的list集合
List<Record> updateList = new CopyOnWriteArrayList<>();
try {
//把三百万条数据放入ArrayBlockingQueue队列中,防止数据丢失
ArrayBlockingQueue<Record> queue = new ArrayBlockingQueue<Record>(records.size());
queue.addAll(records);
int threadSize = 10;//设置线程数量为10
//使用newFixedThreadPool线程池创建线程
ExecutorService es = Executors.newFixedThreadPool(threadSize);
for (int i = 0; i < threadSize; i++) {
int finalPageNumber = pageNumber;
es.execute(new Runnable() {
@Override
public void run() {
//队列中遍历数据
while (!queue.isEmpty()) {
String person_id = null;
boolean flag = false;
try {
//获取每条数据并取出服务器地址和本地文件全路径
Record record = queue.poll(30, TimeUnit.MINUTES);
person_id = record.getStr("person_id");//该头像所属人员唯一标识
String photo_url = record.getStr("photo_url");//服务器地址路径
String local_photo_url = record.getStr("local_photo_url");//本地全路径
//调用下载图片方法
flag = download(Config.getStr("serverPath") + photo_url, local_photo_url);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (flag) {//当头像下载成功往所要修改状态的list中添加该条数据(注意:因数据量过大,必须执行批量修改方法并且数据库中"person_id"要添加索引)
Record recordUpdateStatus = new Record();
recordUpdateStatus.set("person_id", person_id);
recordUpdateStatus.set("result_status", 1);
updateList.add(recordUpdateStatus);
System.out.println(person_id + ":下载成功");
}
}
}
});
}
es.shutdown();//关闭线程
while (true) {
//片段线程中是否全部执行完,执行完之后拿到updateList并更新到数据库中
if (es.isTerminated()) {
//批量修改数据库状态
Db.use("photo").batchUpdate("user_info", "person_id", updateList, updateList.size());
//关闭数据库连接
ConnectionSqlite.connectionClose();
break;
}
Thread.sleep(200);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试方法:
.
//测试方法
public static void main(String[] args) {
downPng();
}
接下来把整个工具类全量工具类放这,有些APP中或许看不到全量代码。建议用PC查看。
/**
* 使用多线程下载三百万张头像图片
*/
public class DownloadPhoto {
/**
* 调接口把三百万条数据读入数据库中后执行该批量下载方法
*/
public static void downPng() {
long startTime = System.currentTimeMillis();
//从数据库获取总页数
Page<Record> photoDatabaset = getPhotoDatabase(1);
if (photoDatabaset.getList().size() > 0) {
int totalPage = photoDatabaset.getTotalPage();//总页数
//因为一批下载完之后要修改状态,所以下载页码要倒序下载才能保证完全下载
for (int pageNumber = totalPage; pageNumber > 0; pageNumber--) {
Page<Record> photoDatabase = getPhotoDatabase(pageNumber);
List<Record> records = photoDatabase.getList();
//线程安全的list集合
List<Record> updateList = new CopyOnWriteArrayList<>();
try {
//把三百万条数据放入ArrayBlockingQueue队列中,防止数据丢失
ArrayBlockingQueue<Record> queue = new ArrayBlockingQueue<Record>(records.size());
queue.addAll(records);
int threadSize = 10;//设置线程数量为10
//使用newFixedThreadPool线程池创建线程
ExecutorService es = Executors.newFixedThreadPool(threadSize);
for (int i = 0; i < threadSize; i++) {
int finalPageNumber = pageNumber;
es.execute(new Runnable() {
@Override
public void run() {
//队列中遍历数据
while (!queue.isEmpty()) {
String person_id = null;
boolean flag = false;
try {
//获取每条数据并取出服务器地址和本地文件全路径
Record record = queue.poll(30, TimeUnit.MINUTES);
person_id = record.getStr("person_id");//该头像所属人员唯一标识
String photo_url = record.getStr("photo_url");//服务器地址路径
String local_photo_url = record.getStr("local_photo_url");//本地全路径
//调用下载图片方法
flag = download(Config.getStr("serverPath") + photo_url, local_photo_url);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (flag) {//当头像下载成功往所要修改状态的list中添加该条数据(注意:因数据量过大,必须执行批量修改方法并且数据库中"person_id"要添加索引)
Record recordUpdateStatus = new Record();
recordUpdateStatus.set("person_id", person_id);
recordUpdateStatus.set("result_status", 1);
updateList.add(recordUpdateStatus);
System.out.println(person_id + ":下载成功");
}
}
}
});
}
es.shutdown();//关闭线程
while (true) {
//片段线程中是否全部执行完,执行完之后拿到updateList并更新到数据库中
if (es.isTerminated()) {
//批量修改数据库状态
Db.use("photo").batchUpdate("user_info", "person_id", updateList, updateList.size());
//关闭数据库连接
ConnectionSqlite.connectionClose();
break;
}
Thread.sleep(200);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 从远程服务器获取文件方法
*
* @param urlList 远程服务器中文件全路径(含文件名)
* @param localUrl 本地文件全路径
* @return
* @throws IOException
*/
public static Boolean download(String urlList, String localUrl) throws IOException {
URL url = null;
Boolean b = true;
DataInputStream dataInputStream = null;
FileOutputStream fileOutputStream = null;
try {
url = new URL(urlList);
dataInputStream = new DataInputStream(url.openStream());
//从配置文件中获取服务器ip
String localPath = Config.getStr("localPath");
//判断本地路径是否存在,不存在则创建路径
File file = new File(localPath);
if (!file.exists()) {
file.mkdirs();
}
fileOutputStream = new FileOutputStream(new File(localUrl));
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = dataInputStream.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
fileOutputStream.write(output.toByteArray());
} catch (IOException e) {
e.printStackTrace();
b = false;
} finally {
dataInputStream.close();
fileOutputStream.close();
}
return b;
}
/**
* 分批获取数据(单批获取10万条数据)
*
* @return
*/
public static Page<Record> getPhotoDatabase(int pageNumber) {
//启动数据库连接
ConnectionSqlite.conection();
Page<Record> mc = Db.use("photo").paginate(pageNumber, 100000, "select person_id,photo_url,local_photo_url", "from" +
" user_info where result_status=0");
return mc;
}
//测试方法
public static void main(String[] args) {
downPng();
}
}