参考文章:https://www.ibm.com/developerworks/cn/java/joy-down/

1.原理介绍

想象一下我们下载一个10G的文件,当下载到9.99G的时候断网了。。。

断点续传支持从文件上次中断的地方开始传送数据,而并非是从文件开头传送。

体现在HTTP请求和响应上就会有区别:

假设下载文件为:

http://dl_dir.qq.com/invc/cyclone/QQDownload_Setup_39_708_p1.exe

普通文件下载的请求和响应

断点续传时候的请求和响应

这地方需要拦截HttpURLConnection的请求。

2.如何用Java实现

主要利用的是RandomAccessFile类的seek方法。

RandomAccessFile的特点在于任意访问文件的任意位置,是基于字节访问的;
可通过getFilePointer()获取当前指针所在位置 ;
可通过seek()移动指针,这体现了它的任意性;
seek用于设置文件指针位置,设置后会从当前指针的下一位读取或写入。

这地方我在IBM文章基础上简化并调整了一版,自测了下。搬上来分享一下。

 

2.1 工具类

package com.laoxu.demo.breaktransfer;

public class Utility {
public Utility()
{
}
public static void sleep(int nSecond)
{
try{
Thread.sleep(nSecond);
}
catch(Exception e)
{
e.printStackTrace ();
}
}
public static void log(String sMsg)
{
System.err.println(sMsg);
}
public static void log(int sMsg)
{
System.err.println(sMsg);
}
}

2.2 文件信息实体

package com.laoxu.demo.breaktransfer;

/**
* 远程下载文件信息
*/
public class SiteFileInfo {
private String sSiteURL; //文件下载链接
private String sFilePath; //保存的文件路径
private String sFileName; //保存的文件名称
public SiteFileInfo()
{
this("","","");
}
public SiteFileInfo(String sURL, String sPath, String sName)
{
sSiteURL= sURL;
sFilePath = sPath;
sFileName = sName;
}
public String getSSiteURL()
{
return sSiteURL;
}
public void setSSiteURL(String value)
{
sSiteURL = value;
}
public String getSFilePath()
{
return sFilePath;
}
public void setSFilePath(String value)
{
sFilePath = value;
}
public String getSFileName()
{
return sFileName;
}
public void setSFileName(String value)
{
sFileName = value;
}
}

2.3 写文件类

package com.laoxu.demo.breaktransfer;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable; /**
* 写文件
*/
public class FileAccess implements Serializable{
RandomAccessFile oSavedFile;
long nPos;
public FileAccess() throws IOException
{
this("",0);
}
public FileAccess(String sName, long nPos) throws IOException
{
oSavedFile = new RandomAccessFile(sName,"rw");
this.nPos = nPos;
oSavedFile.seek(nPos);
}
public synchronized int write(byte[] b,int nStart,int nLen)
{
int n = -1;
try{
oSavedFile.write(b,nStart,nLen);
n = nLen;
}
catch(IOException e)
{
e.printStackTrace ();
}
return n;
}
}

2.4 主程序

package com.laoxu.demo.breaktransfer;

import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL; /**
* @Description: 断点续传主程序
* @Author laoxu
* @Date 2019/12/7 15:31
**/
public class SiteFileFetch{
SiteFileInfo fileInfo = null; // 文件信息实体
long startPos; // 开始位置
long endPos; // 结束位置
long fileLength; // 文件长度 File localFile = null; // 保存的文件
String localFilePath; // 本地文件路径
boolean bFirst = true; // 是否第一次取文件
boolean bStop = false; // 停止标志 public SiteFileFetch(SiteFileInfo entity){
fileInfo = entity;
localFilePath = entity.getSFilePath()+File.separator+entity.getSFileName();
localFile = new File(localFilePath);
// 判断本地文件是否存在
if(localFile.exists()){
startPos = localFile.length();
bFirst = false;
}
fileLength = getFileSize(); if(fileLength < 0){
System.err.println("非法文件!");
}else{
endPos = fileLength;
} } public void download() {
if(startPos<endPos){
try {
FileAccess fileAccess = new FileAccess(localFilePath,startPos); // 配置fiddler代理
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888));
URL url = new URL(fileInfo.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(proxy); httpConnection.setRequestProperty("User-Agent", "LX");
String sProperty = "bytes=" + startPos + "-";
httpConnection.setRequestProperty("RANGE", sProperty);
Utility.log(sProperty);
InputStream input = httpConnection.getInputStream();
//logResponseHead(httpConnection);
byte[] b = new byte[1024];
int nRead;
while ((nRead = input.read(b, 0, 1024)) > 0 && startPos < endPos
&& !bStop) {
startPos += fileAccess.write(b, 0, nRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}else {
System.out.println("文件已存在!");
} } // 获得文件长度
private long getFileSize() {
int nFileLength = -1;
try {
URL url = new URL(fileInfo.getSSiteURL());
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestProperty("User-Agent", "Lx");
int responseCode = httpConnection.getResponseCode();
if (responseCode >= 400) {
System.out.println(responseCode);
return -2; //-2 represent access is error
}
String sHeader;
for (int i = 1; ; i++) {
sHeader = httpConnection.getHeaderFieldKey(i);
if (sHeader != null) {
if (sHeader.equals("Content-Length")) {
nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));
break;
}
} else
break;
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Utility.log(nFileLength);
return nFileLength;
}
}

2.5 测试程序

package com.laoxu.demo.breaktransfer;

public class TestMethod {
public TestMethod() {
try {
SiteFileInfo fileInfo = new SiteFileInfo("http://dl_dir.qq.com/invc/cyclone/QQDownload_Setup_39_708_p1.exe",
"D:\\tmp",
"QQDownload_Setup_39_708_p1.exe");
SiteFileInfo fileInfo2 = new SiteFileInfo("http://www.luohanye.com/astros.json",
"D:\\tmp",
"astros.json");
SiteFileFetch fileFetch = new SiteFileFetch(fileInfo);
fileFetch.download();
} catch (Exception e) {
e.printStackTrace();
}
} public static void main(String[] args) {
new TestMethod();
}
}

Java实现文件下载断点续传(一)的更多相关文章

  1. java大文件下载+断点续传

    java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路:1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...

  2. php大文件下载+断点续传

    如果我们的网站提供文件下载的服务,那么通常我们都希望下载可以断点续传(Resumable Download),也就是说用户可以暂停下载,并在未来的某个时间从暂停处继续下载,而不必重新下载整个文件. 通 ...

  3. 用java实现文件下载,提示java.lang.IllegalStateException: getOutputStream() has already been called for this response

    1. 用java实现文件下载,提示java.lang.IllegalStateException: getOutputStream() has already been called for this ...

  4. 【Servlet】java web 文件下载功能实现

    需求:实现一个具有文件下载功能的网页,主要下载压缩包和图片 两种实现方法: 一:通过超链接实现下载 在HTML网页中,通过超链接链接到要下载的文件的地址 <!DOCTYPE html> & ...

  5. C# 文件下载断点续传

    C# 文件下载断点续传的一个类 using System; using System.Collections.Generic; using System.Linq; using System.Text ...

  6. Java Web文件下载

    Web文件下载有两种.一种是文件在站点文件夹下.在浏览器中直接输入文件路径就可以下载.如http://www.xxx.com/file.zip.第二种是文件不在站点文件夹下或者文件是动态生成的(导出报 ...

  7. Java单线程文件下载,支持断点续传功能

    前言: 程序下载文件时,有时会因为各种各样的原因下载中断,对于小文件来说影响不大,可以快速重新下载,但是下载大文件时,就会耗费很长时间,所以断点续传功能对于大文件很有必要. 文件下载的断点续传: 1. ...

  8. java实现多线程断点续传,上传下载

    采用apache 的 commons-net-ftp-ftpclient import java.io.File; import java.io.FileOutputStream; import ja ...

  9. java+web+下载断点续传

    1.先将 webuploader-0.1.5.zip 这个文件下载下来:https://github.com/fex-team/webuploader/releases  根据个人的需求放置自己需要的 ...

  10. java+HTML5实现断点续传

    一. 大文件上传基础描述: 各种WEB框架中,对于浏览器上传文件的请求,都有自己的处理对象负责对Http MultiPart协议内容进行解析,并供开发人员调用请求的表单内容. 比如: Spring 框 ...

随机推荐

  1. [转帖]MySQL8.1来了:MySQL创新和长期支持(LTS)版本简介

    https://cloud.tencent.com/developer/article/2303772 在Oracle,我们不断寻找改进产品的方法,以更好地满足您的需求.我们很高兴推出MySQL创新和 ...

  2. [转帖]k8s集群部署工具kubeadm详解

    https://zhuanlan.zhihu.com/p/670125857 kubeadm是快捷创建Kubernetes集群的最佳实践工具,我们只需用kubeadm init 和 kubeadm j ...

  3. [转帖]金仓数据库KingbaseES V8R6索引坏块故障处理

    案例说明: 在执行表数据查询时,出现下图所示错误,索引故障导致表无法访问,后重建索引问题解决.本案例复现了此类故障解决过程. 适用版本: KingbaseES V8R3/R6 一.创建测试环境 # 表 ...

  4. [转帖]OS Watcher (OSW)系统性能监控软件

    https://www.anbob.com/archives/1143.html OS Watcher简称OSW(oswbb),用于收集并归档操作系统cpu,memery,disk io,networ ...

  5. 【转帖】一文解析ethtool 命令的使用

    命令简介 ethtool命令用于查询和控制网络设备驱动程序和硬件设置,尤其是有线以太网设备,devname网卡的名称.网卡就像是交换机的一个端口,正常使用我们只是配置网卡IP地址等信息,网卡的速率.双 ...

  6. 【转帖】【奇技淫巧】Linux | 统计网络-netstat

    theme: condensed-night-purple 小知识,大挑战!本文正在参与"程序员必备小知识"创作活动. 在构建生产服务器时,我们有的时候需要统计网络接口状况,比如T ...

  7. K8S 知识点

    1. K8S集群大小 在 v1.7 版本中,Kubernetes 支持集群节点(node)数可达1000个.更具体地说,我们配置能够支持所有如下条件: 不超过2000个节点 不超过总共6000个 po ...

  8. STM32CubeMX教程24 WDG - 独立窗口看门狗

    1.准备材料 开发板(正点原子stm32f407探索者开发板V2.4) STM32CubeMX软件(Version 6.10.0) 野火DAP仿真器 keil µVision5 IDE(MDK-Arm ...

  9. 兼容微信支付宝抖音小程序的工具推荐!还能将小程序搬到自己的app里面

    事情的起因是这样的. 之前在微信.支付宝和抖音开放平台都上架了自己的小程序,虽然几个平台有自己的开发标准,但是都是基于 JavaScript 这种已经被广泛使用的编程语言进行开发的,对于开发者而言学习 ...

  10. 01 vue子组件调用父组件中的方法

    vue子组件,调用父组件中有三种方法哈!下面我们一起来讲解. 第一种使用 直接在子组件中通过this.$parent.父组件中的方法.来调用父组件的方法 第一种的缺点是不能够传递参数哈.它只能够调用方 ...