Java多线程文件下载
Posted 凉城
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程文件下载相关的知识,希望对你有一定的参考价值。
以下代码有点问题,会发生阻塞,还不知道啥问题:
package com.test.service; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CountDownLatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * <p> * 多线程文件下载,提高大文件的下载速度(暂未使用) * <p> * */ @Component public class MulitThreadDownload { private static Logger logger = LoggerFactory.getLogger(MulitThreadDownload.class); @Value("${onair.download.threadsize:5}") private int threadSize = 5; @Value("${onair.download.timeout:5000}") private int downloadTimeout; static boolean flag = true; //消息 private final CountDownLatch msgDownLatch = new CountDownLatch(1); //工作线程 private final CountDownLatch workDownLatch = new CountDownLatch(threadSize); private DowloadRunnable[] dowloadRunnables = new DowloadRunnable[threadSize]; public static void main(String[] args) { new MulitThreadDownload().downloadFile("", "G:\\123.mp4"); } public boolean downloadFile(String url,String filePath){ logger.debug("下载地址:{},目标文件路径:{}",url,filePath); try { URL urlPath = new URL(url); HttpURLConnection conn = (HttpURLConnection)urlPath.openConnection(); conn.setConnectTimeout(downloadTimeout); conn.setRequestMethod("GET"); int status = conn.getResponseCode(); if(status == 200){ //200返回所有,206返回部分 //文件长度 int length = conn.getContentLength(); logger.info("获取文件大小:{}",length); //创建下载文件 指定大小 RandomAccessFile raf = new RandomAccessFile(new File(filePath), "rwd"); raf.setLength(length); raf.close(); //释放资源 //分块大小 int blockSize = length / threadSize; //创建工作线程 for (int i = 1; i <= threadSize; i++) { int startIndex = blockSize*(i-1); int endIndex = blockSize * i - 1; if(i == threadSize){ endIndex = length; } logger.info("线程:{}下载文件开始点:{}结束点:{}",i,startIndex,endIndex); dowloadRunnables[i-1] = new DowloadRunnable(url,filePath,msgDownLatch, workDownLatch, i,startIndex,endIndex); Thread thread = new Thread(dowloadRunnables[i-1]); thread.start(); thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { logger.debug("catch到异常",e); flag = false; } }); } //通知工作线程启动,开始工作 msgDownLatch.countDown(); logger.debug("主线程阻塞,等待工作线程完成任务"); //起一个线程监控下载进度 //moniterLength(length); //阻塞主线程,等待工作线程完成 workDownLatch.await(); logger.debug("工作线程完成任务,主线程继续"); return flag; } } catch (Throwable e) { logger.error("文件下载失败:"+e.getMessage(),e); File file = new File(filePath); if(file.exists()){ file.delete(); //下载失败 删除临时文件 } } return false; } //输出下载进度 private void moniterLength(int length) { new Thread(new Runnable() { @Override public void run() { while(getDownloadLength() < length){ logger.debug("文件大小:{},目前下载大小:{},进度{}",length,getDownloadLength(),getDownloadLength()* 1.0 / (long)length); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } //监控下载进度 public int getDownloadLength(){ int length = 0; for (int i = 0; i < dowloadRunnables.length; i++) { length += dowloadRunnables[i].downloadLength; } return length; } } //下载线程 class DowloadRunnable implements Runnable{ private static Logger logger = LoggerFactory.getLogger(DowloadRunnable.class); private CountDownLatch msgDownLatch; private CountDownLatch workDownLatch; private int threadIndex; private int startIndex; private int endIndex; private String url; private String filePath; public int downloadLength; //已下载大小 public DowloadRunnable(String url, String filePath, CountDownLatch msgDownLatch, CountDownLatch workDownLatch, int threadIndex, int startIndex, int endIndex) { this.url = url; this.filePath = filePath; this.msgDownLatch = msgDownLatch; this.workDownLatch = workDownLatch; this.threadIndex = threadIndex; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run() { try { //阻塞此线程,等待主线程给启动消息(msgDownLatch.countDown()); msgDownLatch.await(); //具体工作 logger.info("线程{}任务开始",threadIndex); URL urlPath = new URL(url); HttpURLConnection conn = (HttpURLConnection)urlPath.openConnection(); conn.setConnectTimeout(5000); conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); conn.setRequestMethod("GET"); int status = conn.getResponseCode(); logger.debug("线程{}请求返回的responseCode:{}",threadIndex,status); if(status==206){ InputStream in = conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(filePath, "rwd"); raf.seek(startIndex); byte[] buffer = new byte[2048]; int length = 0; logger.debug("线程{}开始写数据,开始点{}",threadIndex,startIndex); while((length = in.read(buffer)) != -1){ //logger.debug("线程{}读取大小:{}",threadIndex,length); raf.write(buffer, 0, length); //downloadLength += length; } raf.close(); in.close(); }else{ logger.error("文件下载失败,状态码:"+status); throw new Exception("文件下载失败,状态码:"+status); } logger.info("线程{}任务完成",threadIndex); //工作完成 workDownLatch.countDown(); } catch (Throwable e) { logger.error(e.getMessage(),e); e.printStackTrace(); } } }
看不出来啥问题,先记下来!
以上是关于Java多线程文件下载的主要内容,如果未能解决你的问题,请参考以下文章