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();
            }
    }
}
MulitThreadDownload.java

看不出来啥问题,先记下来!

以上是关于Java多线程文件下载的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程与并发库高级应用-工具类介绍

Java多线程与并发库高级应用-工具类介绍

多线程 Thread 线程同步 synchronized

Java多线程具体解释

Android 多线程下载,断点续传,线程池

什么是JAVA的多线程?