【Java】之 写个线程下载文件(支持断点续传)

本文分三次完成。


Step 1

实现功能:
  实现基本下载,参数封装。
学习方面:
  IO,URL, URLConnection, HTTPsConnection
public class DownUtils {

    private final String url;   
    private final String fileName;
    private final String targetDir;

    public DownUtils(String url, String fileName, String targetDir){
        this.url = url;
        this.fileName = fileName;
        this.targetDir = targetDir;
    }

    public DownUtils(String url,String fileName){
        this.url = url;
        this.fileName = fileName;
        this.targetDir = "";
    }

    public void makeSureDir(){
        File targetDir = new File(this.targetDir);
        if(!targetDir.exists())
            targetDir.mkdir();
    }

    public String getFileName(){
        if (!"".equals(targetDir)){
            makeSureDir();
            return targetDir + File.separator + fileName;
        }
        return fileName;
    }

    public void down(){
        try(
            RandomAccessFile raf = new RandomAccessFile(getFileName(), "rw")
        )
        {
            URL url = new URL(this.url);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setConnectTimeout(8000);
            connection.setRequestMethod("GET");
            //防止屏蔽程序抓取而返回403错误
            connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            connection.setRequestProperty("Accept",
                "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                    + "application/x-shockwave-flash, application/xaml+xml, "
                    + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                    + "application/x-ms-application, application/vnd.ms-excel, "
                    + "application/vnd.ms-powerpoint, application/msword, */*");
            connection.setRequestProperty("Accept-Language", "zh-CN");
            connection.setRequestProperty("Charset", "UTF-8");
            InputStream inputStream = connection.getInputStream();
            byte[] buffer = new byte[1024];
            int hasRead = 0;

            while((hasRead = inputStream.read(buffer)) > 0){
                raf.write(buffer, 0, hasRead);
            }
            System.out.println("info:" + url + " download success");
        }
        catch (IOException e){
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {

        DownUtils downUtils = new DownUtils("http://101.95.48.97:8005/res/upload/interface/apptutorials/manualstypeico/6f83ce8f-0da5-49b3-bac8-fd5fc67d2725.png"
            , "yyf.png", "C:\\Users\\donal\\Desktop\\tools");
        downUtils.down();

    }

}

Step 2

实现功能:
  多线程下载
学习方面:
  thread
思想:按线程数将一个文件分成若干份,每个线程执行其中的一份。
package cn.Down;

/**
 * Created by donal on 2017/2/21.
 */

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *  实现多线程下载文件,支持断点续传
 *
 *  1.多线程
 *  2.下载:IO流
 *  3.断点续传:RandomAccessFile
 *  4.下载:URL
 *
 *  step1: 实现文件下载
 *  (step1,2 同时实现代码封装、整洁)
 *  step2: 多线程下载
 *  step3: 实现断点续传
 */
public class DownUtils {

    private final int fileSize;
    private final String url;
    private final String fileName;
    private final String targetDir;
    private final int threadSize;
    private final ExecutorService executorService;

    public DownUtils(String url, String fileName, String targetDir, int threadSize){
        this.url = url;
        this.fileName = fileName;
        this.targetDir = targetDir;
        this.threadSize = threadSize;
        executorService = Executors.newFixedThreadPool(threadSize);
        fileSize = getConnection(url).getContentLength();
    }

    public DownUtils(String url,String fileName, int threadSize){
        this.url = url;
        this.fileName = fileName;
        this.threadSize = threadSize;
        this.targetDir = "";
        executorService = Executors.newFixedThreadPool(threadSize);
        fileSize = getConnection(url).getContentLength();
    }

    public DownUtils(String url, String fileName){
        this.url = url;
        this.fileName = fileName;
        this.targetDir = "";
        this.threadSize = 1;
        executorService = Executors.newFixedThreadPool(threadSize);
        fileSize = getConnection(url).getContentLength();
    }

    public void makeSureDir(){
        File targetDir = new File(this.targetDir);
        if(!targetDir.exists())
            targetDir.mkdir();
    }

    public String getFileName(){
        if (!"".equals(targetDir)){
            makeSureDir();
            return targetDir + File.separator + fileName;
        }
        return fileName;
    }

    public static HttpURLConnection getConnection(String urlPath) {
        try {
            URL url = new URL(urlPath);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setConnectTimeout(8000);
            connection.setRequestMethod("GET");
            //防止屏蔽程序抓取而返回403错误
            connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            connection.setRequestProperty("Accept",
                "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                    + "application/x-shockwave-flash, application/xaml+xml, "
                    + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                    + "application/x-ms-application, application/vnd.ms-excel, "
                    + "application/vnd.ms-powerpoint, application/msword, */*");
            connection.setRequestProperty("Accept-Language", "zh-CN");
            connection.setRequestProperty("Charset", "UTF-8");
            return connection;
        } catch (IOException e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public int getFileSize(){
        return fileSize;
    }

    public void download(){
        int partFileSize = fileSize / threadSize + 1;
        int startPos = 0;
        for (int i = 0 ; i < threadSize; ++i){
            executorService.execute(new DownThread(i, startPos, partFileSize));
            startPos += partFileSize;
        }
        executorService.shutdown();
    }

    private class DownThread implements Runnable{

        private final int threadId;
        private final int startPos;
        private final int partFileSize;

        public DownThread(int threadId, int startPos, int partFileSize){
            this.threadId = threadId;
            this.startPos = startPos;
            this.partFileSize = partFileSize;

        }

        public void writePartFile(){
            try (
                InputStream inputStream = getConnection(url).getInputStream();
                RandomAccessFile raf = new RandomAccessFile(getFileName(), "rw")
            )
            {
                byte[] buffer = new byte[1024];
                int hasRead;
                int length = 0;
                inputStream.skip(startPos);
                raf.seek(startPos);
                while((length < partFileSize) && (hasRead = inputStream.read(buffer)) > 0){
                    raf.write(buffer, 0, hasRead);
                    length += hasRead;
                }
            }
            catch (IOException e){
                e.printStackTrace();
            }
        }

        @Override
        public void run(){
            System.out.println("#" + threadId + " is loading...");
            writePartFile();
            System.out.println("#" + threadId + " succeeded!");
        }
    }

}
package cn.Down;

/**
 * Created by donal on 2017/2/23.
 */
public class DownUtilsTest {
    public static void main(String[] args) {
        DownUtils downUtils = new DownUtils("http://p1.gexing.com/G1/M00/C8/77/rBACJlYnjHzxEOY2AAC9B6vX98g105.jpg"
            , "ais.jpg",  5);
        downUtils.download();

        /*new Thread(new Runnable() {
            @Override
            public void run() {
                while(downUtils.getCompleteRate() <1 ){
                    System.out.println("已完成:" + downUtils.getCompleteRate());
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        });*/
    }
}

Step 3

实现功能:
  实现断点续传(只定义了接口)
学习方面:
  File, IO。
思想:按线程名创建对应的临时文件,若要暂停或者退出,则调用setBreakPoint()函数,对临时文件写入startPos,endPos。
疑惑:如何控制线程(比如控制线程生命周期,即destroy)
结果图:

这里写图片描述

package cn.Down;

/**
 * Created by donal on 2017/2/21.
 */

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DownUtils {

    private final long fileSize;
    private final int threadSize;
    private final String url;
    private final String fileName;
    private final String targetDir;
    private final ExecutorService executorService;

    public DownUtils(String url, String fileName, String targetDir, int threadSize){
        this.url = url;
        this.fileName = fileName;
        this.targetDir = targetDir;
        this.threadSize = threadSize;
        executorService = Executors.newFixedThreadPool(threadSize);
        fileSize = getConnection(url).getContentLength();
    }

    public DownUtils(String url,String fileName, int threadSize){
        this.url = url;
        this.fileName = fileName;
        this.threadSize = threadSize;
        this.targetDir = "";
        executorService = Executors.newFixedThreadPool(threadSize);
        fileSize = getConnection(url).getContentLength();
    }

    public DownUtils(String url, String fileName){
        this.url = url;
        this.fileName = fileName;
        this.targetDir = "";
        this.threadSize = 1;
        executorService = Executors.newFixedThreadPool(threadSize);
        fileSize = getConnection(url).getContentLength();
    }

    private void makeSureDir(){
        File targetDir = new File(this.targetDir);
        if(!targetDir.exists())
            targetDir.mkdir();
    }

    public String getFileName(){
        if (!"".equals(targetDir)){
            makeSureDir();
            return targetDir + File.separator + fileName;
        }
        return fileName;
    }

    private static HttpURLConnection getConnection(String urlPath) {
        try {
            URL url = new URL(urlPath);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setConnectTimeout(8000);
            connection.setRequestMethod("GET");
            //防止屏蔽程序抓取而返回403错误
            connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            connection.setRequestProperty("Accept",
                "image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
                    + "application/x-shockwave-flash, application/xaml+xml, "
                    + "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
                    + "application/x-ms-application, application/vnd.ms-excel, "
                    + "application/vnd.ms-powerpoint, application/msword, */*");
            connection.setRequestProperty("Accept-Language", "zh-CN");
            connection.setRequestProperty("Charset", "UTF-8");
            return connection;
        } catch (IOException e){
            e.printStackTrace();
            throw new RuntimeException("get connection default.");
        }
    }

    private File fileFactory(String fileName){
        return new File(fileName);
    }

    private boolean isFileExist(){
        File targetFile = fileFactory(getFileName());
        if (targetFile.exists() && targetFile.length() == fileSize){
            System.out.println("The file you want to download has existed!!");
            return true;
        }
        return false;
    }

    private void createThread(){
        long partFileSize = fileSize / threadSize + 1;
        long startPos = 0;
        for (int i = 0 ; i < threadSize; ++i){
            executorService.execute(new DownThread(i, startPos, partFileSize));
            startPos += partFileSize;
        }
        executorService.shutdown();
    }

    public void download(){
        if (isFileExist())
            return;
        System.out.println(url + "    is ready to download.----------");
        createThread();
    }

    private class DownThread implements Runnable{

        private long threadId;
        private long startPos;
        private long endPos;
        private long partFileSize;
        private final String tempFileName;
        private transient File tempFile;

        public DownThread(int threadId, long startPos,  long partFileSize) {
            this.threadId = threadId;
            this.startPos = startPos;
            this.partFileSize = partFileSize;
            endPos = partFileSize + startPos;
            tempFileName = "temp" + threadId + ".txt";
        }

        public void writePartFile(){
            try (
                InputStream inputStream = getConnection(url).getInputStream();
                RandomAccessFile raf = new RandomAccessFile(getFileName(), "rw")
            )
            {
                byte[] buffer = new byte[1024];
                int hasRead;
                inputStream.skip(startPos);
                raf.seek(startPos);
                int length = 0;
                while((length < partFileSize) && (hasRead = inputStream.read(buffer)) > 0){
                    raf.write(buffer, 0, hasRead);
                    startPos += hasRead;
                }
                tempFile.deleteOnExit();
            }
            catch (IOException e){
                throw new RuntimeException("write part file default.");
            }
        }

        private void readTempFile(){
            try(
                RandomAccessFile raf = new RandomAccessFile(tempFileName, "rw")
            )
            {
                raf.seek(0);
                startPos = raf.readLong();
                raf.seek(8);
                endPos = raf.readLong();
                partFileSize = endPos - startPos;
            }
            catch (IOException e){
                throw new RuntimeException("read from temp file default.");
            }
        }
        private void createTempFile() {
            try {
                tempFile = File.createTempFile(tempFileName, null);
            } catch (IOException e) {
                throw new RuntimeException("create new file default.");
            }
        }

        public void makeSureTempFileExist() {
            tempFile = new File(tempFileName);
            if (tempFile.exists()){
                readTempFile();
            }else {
                createTempFile();
            }
        }

        private void setBreakPoint(){
            try(
                RandomAccessFile raf = new RandomAccessFile(tempFileName, "rw")
                )
            {
                raf.writeLong(startPos);
                raf.writeLong(endPos);
            }
            catch (IOException e){
                e.printStackTrace();
                throw new RuntimeException("set break-point default.");
            }
        }
        @Override
        public void run(){
            System.out.println("#" + threadId + "---Thread is loading...");
            makeSureTempFileExist();
            writePartFile();
            System.out.println("#" + threadId + "---Thread succeeded!");
        }
    }

}
package cn.Down;

/**
 * Created by donal on 2017/2/23.
 */
public class DownUtilsTest {
    public static void main(String[] args) {
        DownUtils downUtils = new DownUtils("https://gss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/d1a20cf431adcbef25b551dfaaaf2edda2cc9f61.jpg"
            , "ais2.jpg",  5);
        downUtils.download();

        /*new Thread(new Runnable() {
            @Override
            public void run() {
                while(downUtils.getCompleteRate() <1 ){
                    System.out.println("已完成:" + downUtils.getCompleteRate());
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        });*/
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值