当前位置: 首页 > 编程笔记 >

java线程池实现批量下载文件

吕鸿朗
2023-03-14
本文向大家介绍java线程池实现批量下载文件,包括了java线程池实现批量下载文件的使用技巧和注意事项,需要的朋友参考一下

本文实例为大家分享了java线程池实现批量下载文件的具体代码,供大家参考,具体内容如下

1 创建线程池

package com.cheng.webb.thread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadUtil {
 
 /**
   * 创建批量下载线程池
   *
   * @param threadSize 下载线程数
   * @return ExecutorService
   */
  public static ExecutorService buildDownloadBatchThreadPool(int threadSize) {
    int keepAlive = 0;
    String prefix = "download-batch";
    ThreadFactory factory = ThreadUtil.buildThreadFactory(prefix);


    return new ThreadPoolExecutor(threadSize,
        threadSize,
        keepAlive,
        TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(threadSize),
        factory);
  }
  
  /**
   * 创建自定义线程工厂
   *
   * @param prefix 名称前缀
   * @return ThreadFactory
   */
  public static ThreadFactory buildThreadFactory(String prefix) {
    return new CustomThreadFactory(prefix);
  }
  
  
  /**
   * 自定义线程工厂
   */
  public static class CustomThreadFactory implements ThreadFactory {

    private String threadNamePrefix;

    private AtomicInteger counter = new AtomicInteger(1);

    /**
     * 自定义线程工厂
     *
     * @param threadNamePrefix 工厂名称前缀
     */
    CustomThreadFactory(String threadNamePrefix) {
      this.threadNamePrefix = threadNamePrefix;
    }

    @Override
    public Thread newThread(Runnable r) {
      String threadName = threadNamePrefix + "-t" + counter.getAndIncrement();
      return new Thread(r, threadName);
    }
  }

}

2 批量下载文件

package com.cheng.webb.thread;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 文件下载类
 * 
 * @author shucheng
 * @creation 2019年1月30日下午4:41:32
 */
public class DownloadUtil {

 private static Logger logger = LoggerFactory.getLogger(DownloadUtil.class);

 /**
 * 下载线程数
 */
 private static final int DOWNLOAD_THREAD_NUM = 14;

 /**
 * 下载线程池
 */
 private static ExecutorService downloadExecutorService = ThreadUtil
  .buildDownloadBatchThreadPool(DOWNLOAD_THREAD_NUM);

 /**
 * 文件下载
 *
 * @param fileUrl
 *      文件url,如:<code>https://img3.doubanio.com//view//photo//s_ratio_poster//public//p2369390663.webp</code>
 * @param path
 *      存放路径,如: /opt/img/douban/my.webp
 */
 public static void download(String fileUrl, String path) {
 // 判断存储文件夹是否已经存在或者创建成功
 if (!createFolderIfNotExists(path)) {
  logger.error("We can't create folder:{}", getFolder(path));
  return;
 }

 InputStream in = null;
 FileOutputStream out = null;
 try {
  URL url = new URL(fileUrl);
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  conn.setRequestMethod("GET");
  // 2s
  conn.setConnectTimeout(10000);
  in = conn.getInputStream();

  out = new FileOutputStream(path);

  int len;
  byte[] arr = new byte[1024 * 1000];
  while (-1 != (len = in.read(arr))) {
  out.write(arr, 0, len);
  }
  out.flush();
  conn.disconnect();
 } catch (Exception e) {
  logger.error("Fail to download: {} by {}", fileUrl, e.getMessage());
 } finally {
  try {
  if (null != out) {
   out.close();
  }
  if (null != in) {
   in.close();
  }
  } catch (Exception e) {
  // do nothing
  }
 }
 }

 /**
 * 创建文件夹,如果文件夹已经存在或者创建成功返回true
 *
 * @param path
 *      路径
 * @return boolean
 */
 private static boolean createFolderIfNotExists(String path) {
 String folderName = getFolder(path);
 if (folderName.equals(path)) {
  return true;
 }
 File folder = new File(getFolder(path));
 if (!folder.exists()) {
  synchronized (DownloadUtil.class) {
  if (!folder.exists()) {
   return folder.mkdirs();
  }
  }
 }
 return true;
 }

 /**
 * 获取文件夹
 *
 * @param path
 *      文件路径
 * @return String
 */
 private static String getFolder(String path) {
 int index = path.lastIndexOf("/");
 return -1 != index ? path.substring(0, index) : path;
 }

 /**
 * 下载资源
 * <p>
 * issue: 线程池创建过多
 * <p>
 * 最大批量下载为5,请知悉
 *
 * @param resourceMap
 *      资源map, key为资源下载url,value为资源存储位置
 */
 public static void batch(Map<String, String> resourceMap) {
 if (resourceMap == null || resourceMap.isEmpty()) {
  return;
 }

 try {
  List<String> keys = new ArrayList<>(resourceMap.keySet());
  int size = keys.size();
  int pageNum = getPageNum(size);
  for (int index = 0; index < pageNum; index++) {
  int start = index * DOWNLOAD_THREAD_NUM;
  int last = getLastNum(size, start + DOWNLOAD_THREAD_NUM);

  final CountDownLatch latch = new CountDownLatch(last - start);
  // 获取列表子集
  List<String> urlList = keys.subList(start, last);
  for (String url : urlList) {
   // 提交任务
   Runnable task = new DownloadWorker(latch, url, resourceMap.get(url));
   downloadExecutorService.submit(task);
  }
  latch.await();
  }
 } catch (Exception e) {
  logger.error("{}", e);
 }
 logger.info("Download resource map is all done");
 }

 /**
 * 获取最后一个元素
 *
 * @param size
 *      列表长度
 * @param index
 *      下标
 * @return int
 */
 private static int getLastNum(int size, int index) {
 return index > size ? size : index;
 }

 /**
 * 获取划分页面数量
 *
 * @param size
 *      列表长度
 * @return int
 */
 private static int getPageNum(int size) {
 int tmp = size / DOWNLOAD_THREAD_NUM;
 return size % DOWNLOAD_THREAD_NUM == 0 ? tmp : tmp + 1;
 }

 /**
 * 下载线程
 */
 static class DownloadWorker implements Runnable {

 private CountDownLatch latch;

 private String url;
 private String path;

 DownloadWorker(CountDownLatch latch, String url, String path) {
  this.latch = latch;
  this.url = url;
  this.path = path;
 }

 @Override
 public void run() {
  logger.debug("Start batch:[{}] into: [{}]", url, path);
  DownloadUtil.download(url, path);
  logger.debug("Download:[{}] into: [{}] is done", url, path);
  latch.countDown();
 }
 }

}

3 测试批量下载文件

package com.cheng.webb.thread;

import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import com.alibaba.fastjson.JSON;

public class DownLoadTest {
 String json = "{\r\n"
  + " \"http://www.xxx.com/111/123.mp4\":\"myFile/111/123.mp4\",\r\n"
  + " \"http://www.xxx.com/111/124.mp4\":\"myFile/111/124.mp4\",\r\n"
  + " \"http://www.xxx.com/111/125.mp4\":\"myFile/111/125.mp4\"\r\n"
  + "}";

 @SuppressWarnings("unchecked")
 @Test
 public void test() {
 Map<String, String> map = new HashMap<>();
 Map<String, String> resMap = JSON.parseObject(json, map.getClass());

 int times = 1;
 for (int index = 0; index < times; index++) {
  DownloadUtil.batch(resMap);
 }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍Python实现批量下载文件,包括了Python实现批量下载文件的使用技巧和注意事项,需要的朋友参考一下 Python实现批量下载文件 其他网友的方法: 以上便是本文给大家分享的全部内容了,小伙伴们可以测试下哪种方法效率更高呢。

  • 本文向大家介绍java多线程实现文件下载,包括了java多线程实现文件下载的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了java多线程实现文件下载的具体代码,供大家参考,具体内容如下 1、DownloadManager类  2、DownloadThread类 3、TestDownload测试类 代码已经测试可以运行! 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多

  • 本文向大家介绍java多线程实现文件下载功能,包括了java多线程实现文件下载功能的使用技巧和注意事项,需要的朋友参考一下 多线程下载文件的思路: 1.首先获取到文件的总大小 获取文件大小的方式是通过网络读取,getContentLength()即可获取到文件的大小,使用RandomAccessFile()支持随机访问 2.根据所准备的线程数据,计算每一个线程需要下载的文件的大小 上图显示下载40

  • 本文向大家介绍java实现批量下载 多文件打包成zip格式下载,包括了java实现批量下载 多文件打包成zip格式下载的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了java实现批量下载的具体代码,供大家参考,具体内容如下 现在的需求的: 根据产品族、产品类型,下载该产品族、产品类型下面的pic包; pic包是zip压缩文件; t_product表: 这些包以blob形式存在另一张表

  • 本文向大家介绍Java多线程下载文件实现案例详解,包括了Java多线程下载文件实现案例详解的使用技巧和注意事项,需要的朋友参考一下 原理解析: 利用RandomAccessFile在本地创建一个随机访问文件,文件大小和服务器要下载的文件大小相同。 根据线程的数量(假设有三个线程),服务器的文件三等分,并把我们在本地创建的文件同样三等分,每个线程下载自己负责的部分,到相应的位置即可。 示例图: 代码

  • 本文向大家介绍java 线程池的实现方法,包括了java 线程池的实现方法的使用技巧和注意事项,需要的朋友参考一下 线程池有以下几种实现方式: Executors目前提供了5种不同的线程池创建配置: 1、newCachedThreadPool() 它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,