Java实现多线程下载文件

这篇博客介绍了两种文件下载的方法:一是使用多线程实现,通过创建多个下载线程并行下载文件;二是利用CompletableFuture进行异步下载,主动设置计算结果并结束阻塞。两种方法都涉及到HTTP连接、文件IO操作,并且考虑了文件的分块下载和合并。
摘要由CSDN通过智能技术生成

 这是本人在实际开发当中遇到的多线程下载文件并记录下来

public class DownloadUtil {
    private String pathFile;
    private String strFile;
    private DownloadThread[] downloadThreadArr;
    private int threadNum;
    private int size;

    public DownloadUtil (String pathFile, String strFile, int threadNum) {
        this.pathFile = pathFile;
        this.threadNum = threadNum;
        downloadThreadArr = new DownloadThread[threadNum];
        this.strFile = strFile;
    }

    public void download() throws Exception {
        URL url = new URL(pathFile);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        conn.setRequestProperty(
                "Accept",
                "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                        + "application/x-shockwave-flash, application/xaml+xml, "
                        + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                        + "application/x-ms-application, application/vnd.ms-excel, "
                        + "application/vnd.ms-powerpoint, application/msword, */*");
        conn.setRequestProperty("Accept-Language", "zh-CN");
        conn.setRequestProperty("Charset", "UTF-8");
        conn.setRequestProperty("Connection", "Keep-Alive");

        size = conn.getContentLength();
        conn.disconnect();
        int currentPartSize = size / threadNum + 1;
        RandomAccessFile file = new RandomAccessFile(strFile, "rw");
        file.setLength(size);
        file.close();
        for (int i = 0; i < threadNum; i++) {
            int start = i * currentPartSize;
            RandomAccessFile currentPart = new RandomAccessFile(strFile, "rw");
            currentPart.seek(start);
            downloadThreadArr[i] = new DownloadThread(start, currentPartSize, currentPart);
            downloadThreadArr[i].start();
        }
    }

    
    class DownloadThread extends Thread {
        private int start;
        private int size;
        private RandomAccessFile out;
        public int length;
        
        public DownloadThread(int start, int size,RandomAccessFile out) {
            this.out = out;
            this.start = start;
            this.size = size;
        }

        @Override
        public void run() {
            InputStream is = null;
            try {
                URL url = new URL(pathFile);
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.setConnectTimeout(5000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty(
                        "Accept",
                        "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " + "application/x-shockwave-flash, application/xaml+xml, " + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                                + "application/x-ms-application, application/vnd.ms-excel, "
                                + "application/vnd.ms-powerpoint, application/msword, */*");
                conn.setRequestProperty("Accept-Language", "zh-CN");
                conn.setRequestProperty("Charset", "UTF-8");
                is = conn.getInputStream();
                is.skip(this.start);
                byte[] bs = new byte[1024];
                int len = 0;
                while ((len = is.read(bs)) != -1 && length < size) {
                    out.write(bs, 0, len);
                    length += len;
                }
                out.close();
                is.close();
            }
            catch (Exception e) {
                e.printStackTrace();
            }finally {
                try {
                    out.close();
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
               
            }
        }
    }

   
}







@RestController
@RequestMapping("tc")
public class TestController {

    @RequestMapping("/download")
    public void download(String filePath,String savePath) {
        try {
            DownloadUtil downloadUtil = new DownloadUtil (filePath,savePath,10);
            downloadUtil.download();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

第二种

使用CompletableFuture能够主动设置计算的结果值,主动结束阻塞等待

@Slf4j
public class DownLoadFile {

	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	/** * 文件来源路径 */
	private String source;
	/** * 目标路径 */
	private String target;
	/** * 每个线程读取字节长度 */
	private Long eachSize;
	/** * 读取文件总大小 */
	private Long totalLength;
	/** * 源文件 */
	private File sourceFile;
	/** * 目标文件 */
	private File targetFile;
	/** * 并行数量 */
	private int parallelism = 3;
	public DownLoadFile(String source, String target) {
		this.source = source;
		this.target = target;
	}

	public void start() throws IOException { 
		sourceFile = new File(source); 
		targetFile = new File(target); 
		totalLength = sourceFile.length();
		if (totalLength >= 1 * 1024 * 1024 * 1024L / 2){//500M内
			log.info("[DownLoadFile--start()]当前文件大小:fileLen="  +  totalLength + "超过了500M");
			return;
		}
		log.info("[DownLoadFile--start()]文件大小:fileLen={}" , totalLength );
		RandomAccessFile raf = new RandomAccessFile(targetFile, "rw"); //
		raf.setLength(totalLength); 
		raf.close();
		eachSize = totalLength / parallelism; /** * 每个线程读取字节长度 */
		CompletableFuture[] completableFutures = 
				IntStream.range(0, parallelism).boxed().map(
						i -> CompletableFuture .runAsync(()
								-> download(i)).whenComplete((result, e)
								-> log.info("下标..." + i))).toArray(CompletableFuture[]::new);
		CompletableFuture.allOf(completableFutures).join(); 
	}

	private void download(Integer index) { 
		log.info("[DownLoadFile--download()] index={} " , index);
		try (FileInputStream is = new FileInputStream(sourceFile); 
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				RandomAccessFile accessFile = new RandomAccessFile(targetFile, "rw")) { //每次读取1024
			byte[] buff = new byte[1024];
			// 获取当前线程读取区间,最后一个读取剩余部分
			int start = (int) (index * eachSize); 
			int end = (int) (index == parallelism - 1 ? totalLength - 1 : (index + 1) * eachSize - 1); 
			int length = end - start + 1;
			int readLength = 0;
			is.skip(start);
			int size = 0;
			//下载文件并写入本地 
			while ((size = is.read(buff)) != -1 && readLength <= length) {
				baos.write(buff, 0, size);
				readLength += size;
			} 
			byte[] readData = baos.toByteArray();
			if (baos.size() > length){//断点续传
				byte[] result = Arrays.copyOf(readData, length);
				accessFile.seek(start);
				accessFile.write(result);
				log.info("[DownLoadFile--download()]结果:result={}" , result.length);
			} else {
				byte[] result = readData;
				accessFile.seek(start);
				accessFile.write(result);
				log.info("[DownLoadFile--download()]结果:result={}" , result.length);
			}
			log.info("[start=" + start + "],[end=" + end + " ]结束.");
		} catch (IOException e) { 
			e.printStackTrace(); 
		} 
	}

	public static void main(String[] args) throws IOException {//文件名唯一
		String formatStart = sdf.format(new Date());
		log.info("formatStart:" + formatStart);
		String p1 = "D:/test/a.docx";
		String p2 = "D:/test/test1/a.docx";
		DownLoadFile downLoadProcessor = new DownLoadFile(p1,p2);
		downLoadProcessor.start();
		String formatEnd = sdf.format(new Date());
		log.info("formatEnd:" + formatEnd);

	}


}

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值