断点续传实现文件下载

package com.zzw.download;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class MultiThreadDownload {

    //规定下载服务器的资源用三条线程去下载
    private static int threadCount =3;
    private static int currentRuningThread=3;
//  private static String path="http://192.168.102.18:8080/file.txt";
    private static String path="http://192.168.102.18:8080/SIE.exe";
    public static void main(String[] args) {
        //向服务器发送请求拿到要下载的文件的长度是多少
        try {
            URL url=new URL(path);
            HttpURLConnection connection=(HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000);
            int code=connection.getResponseCode();
            if (code==200) {
                //拿到文件的长度大小
                int length=connection.getContentLength();

                File file=new File(getFileName(path));

                RandomAccessFile raf=new RandomAccessFile(file, "rw");
                raf.setLength(length);
                raf.close();

                //求出每一个线程下载的大小
                int blockSize=length/threadCount;

                //启动线程去下载文件
                for(int threadId=0;threadId<threadCount;threadId++){
                    int startIndex=threadId*blockSize;
                    int endIndex=(threadId+1)*blockSize-1;
                    if(threadId==(threadCount-1)){
                        endIndex=length-1;
                    }
                    //下载文件
                    new DownloadFilePartThread(threadId, startIndex, endIndex).start();
                    //续传文件



                }                               
            }           
        } catch (Exception e) {
            e.printStackTrace();
        }                   
    }
    private static class DownloadFilePartThread extends Thread{

        private int threadId;//线程的id号
        private int startIndex;//线程下载的开始位置
        private int endIndex;//线程下载的结束位置
        private int currentPosition;//当前线程,下载到的位置


        public  DownloadFilePartThread(int threadId, int startIndex,int endIndex) {

            this.threadId=threadId;
            this.startIndex=startIndex;
            this.endIndex=endIndex;
            currentPosition=startIndex;
        }
        @Override
        public void run() {
            //去干下载  ----下载目标数据
            //需要连接服务器
            System.out.println("第"+threadId+"线程开始下载了:下载 从"+startIndex+"~"+endIndex);   
            try {
                URL url=new URL(path);
                HttpURLConnection connection=(HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                //在多线程下载的时候,每条线程只需要目标文件的一部分数据
                //需要告诉服务器只需要那一段得数据
                //通过设置http的请求头可以实现,告诉服务器,只需要目标端的数据
                File file=new File(getFileName(path));
                RandomAccessFile raf=new RandomAccessFile(file, "rw");
                //startIndex-----endIndex
                connection.setRequestProperty("range", "bytes="+startIndex+"-"+endIndex);
                //获得服务器给返回的目标端的数据
                File ilf=new File(threadId+".position");
                if(ilf.exists()&&ilf.length()>0){
                    BufferedReader br =new BufferedReader(new FileReader(ilf));
                    String vl=br.readLine();
                    //得到当前写到的位置
                    int alreadyWritePosition=Integer.parseInt(vl);
                    //从这个位置接着传输文件
                    connection.setRequestProperty("range", "bytes="+alreadyWritePosition+"-"+endIndex);
                    raf.seek(alreadyWritePosition);

                    System.out.println("文件"+threadId+"下载过");

                } else{
                   connection.setRequestProperty("range", "bytes="+startIndex+"-"+endIndex);
                    raf.seek(startIndex);
                }


                //返回的响应码是206(状态码)-----代表部分数据
                int code=connection.getResponseCode();
                if(code==206){
                    //拿到数据
                    InputStream in=connection.getInputStream();
                    //将得到的数据写入到文件中

                    //要告诉从哪个位置开始写


                    int len=0;
                    byte[] buf=new byte[1024*1024];
                    while((len=in.read(buf))>0){
                        raf.write(buf,0,len);
                        //将实时的位置给记录了下来,记录之后,紧接着要往 文件中继续写
                         currentPosition=currentPosition+len;
                         File info=new File(threadId+".position");

                         //因为要频繁的使用底层文件所以要用Random中的  “rwd”----d写入底层设备
                         RandomAccessFile rf=new RandomAccessFile(info, "rwd");


//                       OutputStream out=new FileOutputStream(info);
//                       out.write((currentPosition+"").getBytes());
                         rf.write(String.valueOf(currentPosition).getBytes());
                         rf.close();

                    }

                    in.close();
                    raf.close();                            

                }
                System.out.println("第"+threadId+"线程下载结束了"); 
                //等待所有的线程都下载完成后再去删除文件
                //弄一个计数器,记住总共多少条线程正在下载,每当下载玩一个线程currentRuningthread就减一
                //当currentRuningthread小于或者等于0的时候代表所有文件下载结束这时将记录下载位置的文件删除

                //因为涉及到线程切换的问题所以我们要进行synchronized(类){}

                synchronized (MultiThreadDownload.class) {

                currentRuningThread--;
                if(currentRuningThread<=0){
                    //将记录下载位置的文件删除
                    File ill=new File(threadId+".position"); 
                    //重命名记录文件
                    ill.renameTo(new File(threadId+".position.finish"));
                    for(threadId=0;threadId<threadCount;threadId++){
                        File illd=new File(threadId+".position.finish");
                        illd.delete();

                    }
                }
            }

            } catch (Exception e) {         
                e.printStackTrace();
            }


        }
    }   
    public static  String getFileName(String path){
        int index=path.lastIndexOf("/");
        return path.substring(index+1);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值