多线程下载(2):断点续传实现

前言
不含断点续传的多线程实现代码见连接
https://blog.csdn.net/m0_48396761/article/details/107734363

1. 断点续传作用

如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度。

2. 示例代码

package com.luoking.mutildownload;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class MutilDownload {

    static String path = "http://i0.hdslb.com/bfs/article/4329308d201fafbb1a9c41e950988901dba4de22.jpg";
    // 定义线程数量
    private static final int threadCount = 3;
    // 存储正在执行的线程数量
    private static int runningThreadCount = 0;

    public static void main(String[] args) throws Exception {

        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        if (conn.getResponseCode() == 200) {
            // 获取服务器文件的大小
            int fileLength = conn.getContentLength();
            runningThreadCount = threadCount;
            // 创建一个大小和要下载的大小一样的文件 提前把空间申请出来
            RandomAccessFile raf = new RandomAccessFile(new File("pic.png"),
                    "rw");
            raf.setLength(fileLength);

            // 算出每个线程下载的大小
            int blockSize = fileLength / threadCount;

            // 计算每个线程下载的开始和结束位置
            for (int i = 0; i < threadCount; i++) {
                // 开始位置
                int startIndex = i * blockSize;
                // 结束位置
                int endIndex = (i + 1) * blockSize - 1;
                if (i == threadCount - 1) {
                    // 最后一个线程
                    endIndex = fileLength - 1;
                }
                // 开启线程去下载文件
                new DownloadThread(startIndex, endIndex, i).start();
            }
        }

    }

    // 定义线程去服务器下载文件
    private static class DownloadThread extends Thread {
        private int startIndex;
        private int endIndex;
        private int threadId;

        // 通过构造方法将线程的开始和结束位置传递进来
        public DownloadThread(int startIndex, int endIndex, int threadId) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }

        public void run() {
            try {
                // 实现断点续传功能-------------------------
                // 读取存储下载位置的文件
                File file = new File(threadId + ".txt");
                if (file.exists() && file.length() > 0) {
                    // 如果该文件存在并且内容的长度大于1则代表上次没有存完
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(new FileInputStream(file)));
                    // 读取上一次存储的位置
                    int line = Integer.parseInt(br.readLine());
                    startIndex = line;
                    // 关闭流
                    br.close();
                }
                // --------------------------------------
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setConnectTimeout(5000);
                conn.setRequestMethod("GET");
                // 设置一个请求头,告诉服务器每个线程下载的开始和结束位置
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);
                // 获取返回的状态码
                // 200代表获取服务器全部资源成功 206代表获取服务器部分资源成功
                if (conn.getResponseCode() == 206) {
                    // 创建随机读写文件对象
                    RandomAccessFile raf = new RandomAccessFile(new File(
                            "pic.png"), "rw");
                    // 设置每个线程从自己的位置开始写
                    raf.seek(startIndex);
                    // 获取服务器返回的资源
                    InputStream in = conn.getInputStream();

                    // 当前线程下载的大小
                    int total = 0;
                    // 把获取到的资源写入文件中
                    int len = -1;
                    // 定义 1MB 个字节的缓冲区
                    byte[] buffer = new byte[1024 * 1024];
                    while ((len = in.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);

                        // 断点续传:记录下载的位置--------------------
                        total += len;
                        // 把当前下载的位置存储下来,下次再下载的时候就可以按照上一次下载的位置继续下载
                        int currentThreadPosition = total + startIndex;
                        // rwd:直接写入底层存储设备(硬盘),不存放到硬盘缓存区中
                        RandomAccessFile positionRaf = new RandomAccessFile(
                                threadId + ".txt", "rwd");
                        positionRaf.write(String.valueOf(currentThreadPosition)
                                .getBytes());
                        positionRaf.close();
                        // ------------------------------------------
                    }
                    // 关闭随机读写文件流 释放资源
                    raf.close();
                    // 下载完毕输出信息
                    System.out.println("下载完毕 --- 线程id: " + threadId);
                    //-----------------------------------------------
                    // 下载完毕后,删除记录下载位置的txt文件
                    synchronized (DownloadThread.class) {
                        runningThreadCount--;
                        if (runningThreadCount == 0) {
                            for (int i = 0; i < threadCount; i++) {
                                new File(i + ".txt").delete();
                            }
                        }
                    }
                    //-------------------------------------------------

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洛君LuoKing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值