利用多线程分段下载远程文件(callable+ExecutorService)

package com.etc.test;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
 * 利用多线程分段下载远程文件
 * @author Administrator
 *
 */
public class CallableDownload{
//指定文件下载的路径
private static final String urlpath="http://download.ydstatic.com/notewebsite/downloads/YNote.exe";//会有点大,稍等一下就下完了
//指定下载线程的个数(分成四段)
private static final int Thread_COUNT=4;
private int fileSize;//请求的文件大小
    private String realFileName;//请求资源的文件名
//创建一个文件,将远程文件写入该文件中
private File filename= new File("E:\\YNote.exe");
//创建Logger对象,以便进行日志记录
private static Logger logger=Logger.getLogger("CallableDownload");
public static void main(String[] args) {
try {
new CallableDownload().dowload();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void dowload() throws Exception{
URL url=new URL(urlpath);
         logger.info(String.format("连接服务器:%s://%s", url.getProtocol(),url.getHost()));
HttpURLConnection conn=(HttpURLConnection)url.openConnection();//打开连接
conn.setRequestMethod("GET");//用get请求
conn.setConnectTimeout(5000);//设置连接的超时时间为5秒
int code=conn.getResponseCode();//得到服务器的响应码
if(code==200){//如果响应码为200,说明请求资源成功
this.fileSize =conn.getContentLength();//请求资源的大小
//conn.getContentLengthLong();
        String[] temp = url.getFile().split("/");
        if (temp.length != 0) {
            this.realFileName = temp[temp.length - 1];//获取文件名
        }
       logger.info(String.format("文件名:%s  大小:%s", this.realFileName, this.fileSize)); 
             //新建一个随机访问的文件
      RandomAccessFile raf=new RandomAccessFile(filename, "rw");//第一个文件参数,第二个为模式
     //设置随机文件大小为服务器端文件的大小
        raf.setLength(fileSize);
        //raf.close();
        //计算每个线程下载的块的大小  
            int blockSize = conn.getContentLength() / Thread_COUNT;  
        List<CallableDownTask> tasks = new ArrayList<>();
        for (int threadId = 1; threadId <= Thread_COUNT; threadId++) {
        CallableDownTask task;
                int startIndex = (threadId - 1) * blockSize;
                int endIndex = threadId * blockSize - 1;
                if (threadId == Thread_COUNT) {
                    endIndex = fileSize;
                }
                logger.info("线程:" + threadId + " 正在下载部分文件 :" + startIndex + "===>" + endIndex);
                task=new CallableDownTask (startIndex, endIndex);
                tasks.add(task);
           }
        logger.info(String.format("正在下载,请稍等……%s",realFileName));
        ExecutorService service = Executors.newFixedThreadPool(Thread_COUNT);
        service.invokeAll(tasks);
        service.shutdownNow();//下载完成后,关闭资源
        filename.renameTo(new File(realFileName));
        logger.info(String.format("完成下载:%s",realFileName));
}
}

/**
* 下载文件的线程用
*/
class CallableDownTask implements Callable<String>{
//线程下载的起始点
private int startdown;
    //线程下载的结束点
private int enddown;
public CallableDownTask(int startdown,int enddown){
this.startdown=startdown;
this.enddown=enddown;
}
public String call() throws Exception {
URL url=new URL(urlpath);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");  
conn.setConnectTimeout(5000);//设置连接的超时时间为5秒
//设置头部的参数,表示此线程是请求服务器资源的某一部分
    conn.setRequestProperty("Range","bytes="+startdown+"-"+enddown);
InputStream is=conn.getInputStream(); 
byte[] buf=new byte[1024];
//新创建一个RandomAccessFile对象
RandomAccessFile raf=new RandomAccessFile(filename, "rw");
//将写文件的指针指向下载的起始点
raf.seek(startdown);
int filelength=0;
/* while ((filelength = is.read(buf)) != -1) {
              raf.write(buf);// 错误的,会导致下载的文件不完整,因为用这个方法,可能带来一个问题,最后一次可能实际写入会比原来的大(最后一次不满1024个字节的时候)
           }*/
        while ((filelength = is.read(buf))!=-1) {//如果没有可用的字节,因为流是在文件的末尾,会返回-1
        raf.write(buf,0,filelength);// 所以还是带上实际的字节数
        }
        raf.close();
is.close();
return Thread.currentThread().getName();
}
}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值