Java多线程下载、断点续传代码分析(学习笔记)

学习分析的是https://blog.csdn.net/qq_33212500/article/details/80667518的代码。

使用多线程下载TIM为例。查API的过程,以及部分分析都写在注释里头了

HttpURLConnection

  • 支持HTTP特定功能的URLConnection。

Class URL

  • Class URL表示统一资源定位符,指向万维网上的“资源”的指针。

openConnection

         public URLConnection openConnection() throws IOException。返回一个URLConnection实例,表示与URL引用的远程对象的URL 


 

package cn.itcast01;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class MutiDownloadTest {
    private static final int    THREAD_COUNT    = 5;
    private static final String    DOWNLOAD_URL    = "https://qd.myapp.com/myapp/qqteam/tim/down/TIM2.3.1_3.exe";
    private static final String    fileName        = "F:\\TIM.exe";
     static final String    filePath        = "F:\\";

    public static void main(String[] args) {

        long fileSize = 0;
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection)new URL(DOWNLOAD_URL).openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(8000);
            connection.setReadTimeout(8000);
            //setConnectTimeout(int timeout) 
            //设置打开与此URLConnection引用的资源的通信链接时使用的指定超时值(以毫秒为单位)。 
            //setReadTimeout(int timeout) 
            //将读取超时设置为指定的超时时间,以毫秒为单位。 
            
            //public int getResponseCode()从HTTP响应消息获取状态代码。
            //HTTP/1.0 200 OK
            //HTTP/1.0 401 Unauthorized 
            
            if (connection.getResponseCode() == 200) {
                //Class RandomAccessFile该类的实例支持读取和写入随机访问文件。
            	//public RandomAccessFile(String name,String mode)创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件。 

                RandomAccessFile randomAccessFile = new RandomAccessFile(fileName , "rw"); 
                //public long getContentLengthLong()返回 content-length标题字段的值为long。 
                fileSize = connection.getContentLengthLong();
                //如果length方法返回的文件的当前长度小于newLength参数,则该文件将被扩展。
                //为传过来文件开辟一块空间
                randomAccessFile.setLength(fileSize);
                randomAccessFile.close();
                
                //为每个线程分配下载任务,每个线程下载9739036
                long eachSize = fileSize/THREAD_COUNT;
                for(int i=0; i<THREAD_COUNT; i++){
                    long startIndex = i*eachSize;
                    long endIndex = (i+1)*eachSize - 1;
                    /**
                     * 当时最后一个线程的时候,endIndex的值就由文件大小
                     */
                    if(i == THREAD_COUNT - 1){
                        endIndex = fileSize;
                    }
				/*
				 * @param 
				 */
//                    Runnable runnable = new DownloadThreadTest(DOWNLOAD_URL,fileName,i, startIndex, endIndex);
//                    new Thread(runnable).start();
                    new Thread(new DownloadThreadTest(DOWNLOAD_URL,fileName,i, startIndex, endIndex)).start();

                }
            }

        } catch (IOException e) {
            e.printStackTrace();
            // log.info(ExceptionUtils.getFullStackTrace(e));
        } finally {
            if(null != connection){
                connection.disconnect();
            }
        }
    }
}

class DownloadThreadTest implements Runnable {

    // private static final Logger log = Logger.getLogger(DownloadThreadTest.class);
	//url下载地址,fileName本地存放地址
    private String url;
    private String fileName;
    //线程序号
    private int  threadId;
    //线程起始点
    private long startIndex;
    private long endIndex;

    private HttpURLConnection httpURLConnection;
    private RandomAccessFile randomAccessFile;
    private InputStream inputStream;

    public DownloadThreadTest(String url, String fileName, int threadId, long startIndex, long endIndex) {
        super();
        this.url = url;
        this.fileName = fileName;
        this.threadId = threadId;
        this.startIndex = startIndex;
        this.endIndex = endIndex;

    }

    @Override
    public void run() {
        RandomAccessFile downThreadStream = null;
        
        //查看临时文件
        File downThreadFile = new File(MutiDownloadTest.filePath, "wpf_thread_"+threadId+".dt");
        try {
            if(downThreadFile.exists()){
            	//???前面生成了一个downThreadFile,为何还要判断是否存在。且不存在以后,后面创建访问流,但是又不用?
                downThreadStream = new RandomAccessFile(downThreadFile,"rwd");
                String startIndex_str = downThreadStream.readLine();
                if(null == startIndex_str || "".equals(startIndex_str)){
                } else {
                    this.startIndex = Long.parseLong(startIndex_str) - 1; // //设置下载起点
                }
            }else {
                downThreadStream = new RandomAccessFile(downThreadFile, "rwd");
            }

            //ts是什么意思???
            httpURLConnection = (HttpURLConnection) new URL(url + "?ts=" + System.currentTimeMillis()).openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setConnectTimeout(8000);
            httpURLConnection.setReadTimeout(8000);
            /**
             * 设置请求范围.
             * 一个键值对:RANGE和bytes的起点到终点
             */
            httpURLConnection.setRequestProperty("RANGE", "bytes=" + startIndex + "-" + endIndex);

            /**
             * 当请求部分数据成功的时候,返回http状态码206
             * 找不到getResponseCode方法
             */
            if (httpURLConnection.getResponseCode() == 206){

                inputStream = httpURLConnection.getInputStream();
                randomAccessFile = new RandomAccessFile(fileName, "rwd");
                /**
                 * 把开始写的位置设置为startIndex,与请求数据的位置一致
                 */
                //seek(long pos) 设置文件指针偏移,从该文件的开头测量,发生下一次读取或写入
                randomAccessFile.seek(startIndex);

                byte[] bytes = new byte[1024];
                int len;
                int total = 0;
                while((len = inputStream.read(bytes)) != -1){
                    total += len;
                    //log.info("线程" + threadId + ":" + total);
                    System.out.println("线程: " +  threadId + ":" + total);
                    randomAccessFile.write(bytes, 0, len);

                     /*
                         * 将当前现在到的位置保存到文件中
                         */
                    downThreadStream.seek(0);
                    //将下载起始点和总数据量写在文件最开始的地方不会覆盖原来的数据吗,而且为什么要用UTF-8
                    downThreadStream.write((startIndex + total + "").getBytes("UTF-8"));
                }


            }
        } catch (IOException e) {
            e.printStackTrace();
            // log.info(ExceptionUtils.getFullStackTrace(e));
        } finally {
        	//如果不要下载了,不要储存了,不要创建访问流了,我就把临时文件downThreadFile删除了
            try {
                if(null != downThreadStream){
                    downThreadStream.close();

                }
                if(null != httpURLConnection){
                    httpURLConnection.disconnect();
                }
                if(null != inputStream){
                    inputStream.close();
                }
                if(null != randomAccessFile){
                    randomAccessFile.close();
                }
                cleanTemp(downThreadFile);
            } catch (Exception e) {
                e.printStackTrace();
                //log.info(ExceptionUtils.getFullStackTrace(e));
            }
        }
    }

    //删除线程产生的临时文件
    private  void cleanTemp(File file){
        file.delete();
    }
}

猜你喜欢

转载自blog.csdn.net/TangXiaoPang/article/details/86625846
今日推荐