Java多线程实现下载三百万头像图片

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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java技术实践

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值