HttpURLConnection下载
1 正常下载
下载数据,与获取服务器的响应正文没有什么不同,都是一些数据。但是,下载的文件可能不是文本,就不能在下载后在控制台打印出来了,而是应该创建一个文件,然后把获取到的响应数据保存到磁盘文件中。
// 创建URL对象,这个URL对应服务器上的一个AVI视频 URL url = new URL("http://localhost/hello/resource/a.avi"); // 获取连接对象 HttpURLConnection httpCon = (HttpURLConnection)url.openConnection(); // 设置请求方法为GET httpCon.setRequestMethod("GET"); // 连接服务器 httpCon.connect(); // 获取服务器响应的状态码 int code = httpCon.getResponseCode(); // 如果响应成功 if(code == 200) { // 获取连接的输入流对象 InputStream in = new BufferedInputStream(httpCon.getInputStream()); // 创建一个输出流对象,对应F:\\aa.avi文件,你应该知道,这会创建文件 OutputStream out = new BufferedOutputStream(new FileOutputStream("F:\\aa.avi")); // 难道你不知道这是在干什么? byte[] buff = new byte[2048]; int len; while((len = in.read(buff)) != -1) { out.write(buff, 0 , len); } out.close(); } // 关闭连接 httpCon.disconnect();
2 断点下载
什么叫断点下载?迅雷就可以断点下载!就是说下载了一半,然后把下载中断,明天打开电脑再继续下载。如果我在下载一个文件,先下载了50%,然后停止下载,然后再继续下载省下的50%。这里有两个问题需要我们处理:
l 需要告诉服务器,我要从资源的某个字节位置开始下载,而不是从头开始下载;
l 下载到的数据要追加到原来已下载文件的尾部,而不是创建新文件。
第二个问题我们应该知道怎么处理,我们在学习IO流时应该知道FileOutputStream类的构造器可以接受一人boolean类型的参数:
new FileOutputStream(“a.txt”, true);
这表示如果a.txt文件不存在,那么创建它;如果a.txt文件存在,那么就把写入到流中的数据追加到a.txt文件的尾部。
第一个问题是需要通过请求头信息来处理,有一个请求头叫range:
l httpCon.addRequestProperty(“range”, “bytes=0-”):表示从请求资源的0下标位置开始下载,直到资源结束;
l httpCon.addRequestProperty(“range”, “bytes=1024-”):表示从请求资源的1024下标位置开始下载,直到资源结束;
l httpCon.addRequestProperty(“range”, “bytes=1024-2048”):表示从请求资源的1024下标位置开始下载,直到资源的2048位置结束;
有了这个请求头,我们就可以通过请求头告诉服务器,从资源的哪个位置开始下载,而不是从头开始下载了。
但是,我们上一次下载了多少呢?我们需要告诉服务器从上一次下载的位置开始下载,但我们怎么知道上一次下载了多少呢?其实很简单就知道了,你下载的数据都写到目标文件中了,也就是那个还没有下载完的文件,一半的文件,你看看它的长度不就知道了么!
File file = new File(“F:\\aa.avi);
long length = file.length();
还有一个问题需要注意,当使用了range头信息之后,就算响应成功了,但是返回的状态码也不会是200,而不是206。
第一步:获取连接对象,设置GET请求
URL url = new URL("http://localhost/hello/resource/a.avi"); HttpURLConnection httpCon = (HttpURLConnection)url.openConnection(); httpCon.setRequestMethod("GET");
第二步:获取已下载字节数,设置range请求头,连接服务器。
// 已下载字节数。默认已下载的字节数为0 long alreadySize = 0; // 获取磁盘上的文件,把服务器上下载来的数据写入这个文件中! File file = new File("F:\\b.avi"); // 判断文件是否存在,如果存在,说明原来下载过,不过可能没有下载完 if(file.exists()) { // 如果文件存在,那么已下载的字节数就是文件的长度 alreadySize = file.length(); } // 添加range请求头,表示从alreadySize开始下载,到最后结束 httpCon.addRequestProperty("range", "bytes=" + alreadySize + "-"); httpCon.connect();
第三步:获取响应状态码,判断是否响应成功,如果响应成功,那么获取未下载的字节数,以及整个资源的字节个数。
<!--EndFragment-->
<!--EndFragment-->
<!--EndFragment-->
// 获取响应状态码 int code = httpCon.getResponseCode(); // 如果响应成功,因为使用了range请求头,那么响应成功的状态码为206,而不是200 if(code == 206) { // 获取未下载的部分 // 本方法用来获取响应正文的大小,但因为设置了range请求头,那么这个方法返回的就是剩余的大小 long unfinishedSize = httpCon.getContentLength(); // 计算总大小!已完成+未完成=整个资源的大小 long size = alreadySize + unfinishedSize;
第四步:获取连接的输入流对象,创建输出流对象,让输出流对象绑定F:\b.avi文件。注意,一定设置为追加数据!
// 获取连接的输入流对象 InputStream in = new BufferedInputStream(httpCon.getInputStream());
// 创建输出流对象,目标为file
OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true));
<!--EndFragment-->
第五步:开始下载,在下载过程中显示已下载的百分比!
byte[] buff = new byte[2048]; int len; while((len = in.read(buff)) != -1) { out.write(buff, 0 , len); // 把每次下载的字节累加到已下载大小中 alreadySize += len; // 用已下载大小和整个资源大小来计算下载的百分比 System.out.printf("%.2f%%\n", alreadySize * 1.0 / size * 100); Thread.sleep(2); } out.close(); System.out.println("成功了!"); } else { System.out.println("下载失败!"); } httpCon.disconnect();
尝试下载一段时间后把程序结束,然后再运行程序!看看最终下载完成后文件是否可以使用!
<!--EndFragment-->
<!--EndFragment-->
<!--EndFragment-->