实现单线程的断点下载

/**
* 实现单线程的断点下载
*/

public class HttpDownloadSingle implements Runnable
{
// 响应状态码
private String responseCode;
// 响应头信息
private Map<String, String> headers = new HashMap<String, String>();
// 下载事件处理类
private DownloadEvent event;
// 下载文件的url
private String url;
// 文件保存在本地的路径
private String outFilePath;
// 缓冲区大小
private int bufferSize;

public HttpDownloadSingle(DownloadEvent event, String url, String outFilePath, int bufferSize)
{
this.event = event;
this.url = url;
this.outFilePath = outFilePath;
this.bufferSize = bufferSize;
}

@Override
public void run()
{
try
{
download();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}

public void download() throws IOException
{
// 下载的文件保存在本地的路径
File outFile = new File(outFilePath);
long finishedSize = 0;
if (outFile.exists())
// 如果文件已经存在,记录文件大小,即已下载大小
finishedSize = outFile.length();

URL download_url = new URL(url);
Socket socket = new Socket();
// 获取主机地址
String host = download_url.getHost();
// 获取端口 如果url地址没有指定端口 会返回-1 给定一个默认端口号80
int port = download_url.getPort() == -1 ? 80 : download_url.getPort();
// 资料路径
String resourcePath = download_url.getPath();
event.state("connecting " + host + ":" + port);
// 设置read超时
socket.setSoTimeout(5000);
// 连接服务器 并且设置连接超时
socket.connect(new InetSocketAddress(host, port), 3000);
event.state("connect successfully");

try
{
// 生成一个request消息,用于查看所下载文件大小和服务器是否支持断点下载
generateHttpRequest(socket, host, resourcePath, finishedSize);

InputStream socketIn = socket.getInputStream();
// 解析响应头信息
analyseResponseHeader(socketIn, event);

// 获取要下载文件的大小
long contentLength = getFileLength();

// 如果已下载的大小大于等于总的文件大小 直接返回
if (contentLength <= finishedSize)
return;

// 如果已下载大小不等于0 且状态码是200(表示服务器不支持断点下载
// Accept-Ranges: bytes也可以用来判断服务器是否支持断点下载) 也直接返回
if (finishedSize > 0 && "200".equals(responseCode))
return;

//206表示支持断点下载 其他状态码就抛出一个运行时异常
if (responseCode.charAt(0) != '2')
throw new RuntimeException("Unsupported response status code");

byte[] buf = new byte[bufferSize];
int n;
FileOutputStream fos = new FileOutputStream(outFile, true);

try
{
while (-1 != (n = socketIn.read(buf)))
{
fos.write(buf, 0, n);
finishedSize += n;
//计算当前已下载百分比
event.percent(finishedSize * 100 / contentLength);
}
}
finally
{
if (fos != null)
{
try
{
fos.close();
}
catch (Exception e)
{
}
}
}
}
finally
{
socket.close();
}
}

private String getHeader(String name)
{
return headers.get(name);
}

private int getIntHeader(String name)
{
return Integer.parseInt(getHeader(name).trim());
}

private long getFileLength()
{
long len = -1;
try
{
len = getIntHeader("Content-Length");
String[] parts = getHeader("Content-Range").split("/");
if (parts.length > 1)
len = Integer.parseInt(parts[1].trim());
else
len = -1;
}
catch (Exception e)
{
}
return len;
}

private void analyseResponseHeader(InputStream socketIn, DownloadEvent event) throws IOException
{
String line = "";
while (true)
{
int bt = socketIn.read();
if (bt == '\r')
{
bt = socketIn.read();
if (bt == '\n')
{
//如果读到空行,就结束解析
if ("".equals(line))
break;
//解析状态行和头信息
addHeader2Map(line);
event.viewHeader(line);
line = "";
}
}
else
line += (char) bt;
}
}

private void addHeader2Map(String line)
{
int index = line.indexOf(":");
if (index > 0)
headers.put(line.substring(0, index).trim(), line.substring(index + 1));
else
analyseResponseLine(line);
}

private void analyseResponseLine(String line)
{
String[] parts = line.split(" +");
if (parts.length > 1)
//获取状态码
responseCode = parts[1].trim();
}

private void generateHttpRequest(Socket socket, String host, String path, long startPos) throws IOException
{
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println("GET " + path + " HTTP/1.1");
writer.println("Host: " + host);
writer.println("User-Agent: java");
if (startPos > 0)
//如果已下载长度不为0 就通过Range头把已下载长度也一起发送给服务器
writer.println("Range: bytes=" + startPos + "-");
writer.println("Connection: close");
writer.println();
writer.flush();
}

public interface DownloadEvent
{
void state(String message);

void viewHeader(String header);

void percent(long newValue);
}
}


class DownloadProgress implements DownloadEvent
{
long oldValue = -1;

@Override
public void percent(long newValue)
{
if (newValue > oldValue)
{
System.out.print("[" + newValue + "%]");
oldValue = newValue;
}
}

@Override
public void state(String message)
{
System.out.println(message);
}

@Override
public void viewHeader(String header)
{
System.out.println(header);
}
}

/*
* 6688768 bytes 87494656 bytes 121552303 bytes
*/
public class Main
{
public static void main(String[] args) throws Exception
{

if (args.length < 1)
{
System.out.println("Usage: java class DownloadFileName");
return;
}
FileInputStream fis = new FileInputStream(args[0]);
BufferedReader fileReader = new BufferedReader(new InputStreamReader(fis));
String s = "";
String[] ss;
while ((s = fileReader.readLine()) != null)
{
ss = s.split("[ ]+");
if (ss.length > 2)
{
System.out.println("---------------------------");
System.out.println("downloading:" + ss[0]);
System.out.println("output file path:" + ss[1]);
System.out.println("buffer size:" + ss[2]);
System.out.println("---------------------------");
HttpDownloadSingle httpDownload = new HttpDownloadSingle(new DownloadProgress(), ss[0], ss[1], Integer
.parseInt(ss[2]));
new Thread(httpDownload).start();
}
}
fileReader.close();
}
}


运行结果
---------------------------
downloading:http://localhost:8080/test_web/xx.exe
output file path:src/xx.exe
buffer size:4096
---------------------------
connecting localhost:8080
connect successfully
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"607195608-1397026780797"
Last-Modified: Wed, 09 Apr 2014 06:59:40 GMT
Content-Type: application/octet-stream
Content-Length: 607195608
Date: Wed, 16 Apr 2014 01:42:20 GMT
Connection: close
[0%][1%][2%][3%][4%][5%][6%][7%][8%][9%][10%][11%][12%][13%][14%][15%][16%][17%][18%]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值