一、InetAddress
java提供了InetAddress代表IP地址,它有两个子类Inet4Address和Inet6Address,分别代表了IPV4和IPV6地址。
这个类没有构造器,而是使用两个静态工厂方法来获得InetAddress实例
getByName(String host);
getByAddress(byte[] addr);
它的四个主要实例方法
String getCanonicalHostName();取得该IP地址的全限定域名
String getHostAddress();取得IP地址
String getHostName();取得该IP地址的主机名
Boolean isReachable();判断该IP地址是否可达,可以传入一个等待时长作为参数
二、URLDecoder 和 URLEncoder
这两个类用于完成普通字符串与application/x-www-form-urlencoded MIME 字符串之间的相互转换。
什么是application/x-www-form-urlencoded MIME 字符串?
当URL地址里包含非西欧字符的字符串时,系统会将这些非西欧字符转化为特殊的字符串,所以后台接受到的URL需要经过解码转换为普通字符串,这就用到了URLDecoder;当程序发送请求时,需要使用URLEncoder将URL普通字符串编码转换为特殊字符串。
解码和编码分别使用这两个类的静态方法decode(String s , String enc) 、encode(String s ,String enc)来完成。enc为字符集。
三、URL、URLConnection和URLPermission
这三个应该属于应用层的API
下面实现一个多线程下载的工具类来试用一下这个API
参考 HTTP请求头详解
package net; import java.io.File; import java.io.FileReader; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; public class DownUtil { private String path; //下载资源的路径 private String targetFile; // private int threadNum; private DownThread[] threads; private int filesize; private final String infoFileSuffix = "INI.txt"; public DownUtil(String path, String targetFile, int threadNum) { this.path = path; this.targetFile = targetFile; this.threadNum = threadNum; } public DownUtil(String path, String targetFile) { this.path = path; this.targetFile = targetFile; this.threadNum = Runtime.getRuntime().availableProcessors();//默认开启当前机器CPU数量的线程 } public void download() throws Exception{ //建立http请求取得文件大小 URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept","*/*"); conn.setRequestProperty("Accept-Language","zh-CN"); conn.setRequestProperty("Charset","UTF-8"); conn.setRequestProperty("Connection","Keep-Alive"); filesize = conn.getContentLength(); //取得文件大小 conn.disconnect(); //将下载任务分配给每个线程 int currentPartSize = filesize / threadNum + 1; //每个线程负责的文件块的大小 Path path = Paths.get(targetFile+infoFileSuffix); if(Files.exists(path)){ //读取文件内容继续下载 List<String> allLine = Files.readAllLines(path, Charset.forName("UTF-8")); for(int i = 0 ; i < threadNum ; i++){ int startPos = i * currentPartSize; RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw"); currentPart.seek(startPos); threads[i] = new DownThread(startPos,currentPartSize,currentPart,Integer.parseInt(allLine.get(i).toString())); threads[i].start(); } }else { RandomAccessFile file = new RandomAccessFile(targetFile,"rw"); file.setLength(filesize); file.close(); List<String> infoStrings = new ArrayList<>(); for(int i = 0 ; i < threadNum ; i++){ infoStrings.add("0"); } Files.write(path,infoStrings,Charset.forName("UTF-8")); for(int i = 0 ; i < threadNum ; i++){ int startPos = i * currentPartSize; RandomAccessFile currentPart = new RandomAccessFile(targetFile,"rw"); currentPart.seek(startPos); threads[i] = new DownThread(startPos,currentPartSize,currentPart); threads[i].start(); } //重新写入文件 } } private class DownThread extends Thread{ private int startPos; //当前线程开始下载的位置 private int currentPartSize; //当前线程负责下载的大小 private RandomAccessFile currntPart; //当前线程需要下载的文件类 public int length; //已下载的字节数,如果要实现断点下载,可以吧这个数据存在硬盘上,即存在文件里 private final String acceptString = "*/*"; private final String charSetString = "UTF-8"; private final String acceptLanguageString = "zh-CN"; public DownThread(int startPos, int currentPartSize, RandomAccessFile currntPart,int length) { this.startPos = startPos; this.currentPartSize = currentPartSize; this.currntPart = currntPart; this.length = length; } public DownThread(int startPos, int currentPartSize, RandomAccessFile currrntPart){ this.startPos = startPos; this.currentPartSize = currentPartSize; this.currntPart = currrntPart; this.length = 0; } @Override public void run() { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept",acceptString); conn.setRequestProperty("Charset",charSetString); conn.setRequestProperty("Accept-Language",acceptLanguageString); conn.setRequestProperty("Range","bytes=" + (startPos + length) + "-" + (startPos + currentPartSize - 1)); InputStream inputStream = conn.getInputStream(); byte[] buffer = new byte[1024]; int hasRead = 0; //一次循环读取的字节数 while (length < currentPartSize && (hasRead = inputStream.read(buffer)) != -1){ currntPart.write(buffer,0,hasRead); length += hasRead; } currntPart.close(); inputStream.close(); }catch (Exception e){ e.printStackTrace(); } } } }
以上代码还可以增加断点续传下载功能,这段代码基本上展示了URL和URLconniction(抽象类)这两个类的用法
而URLPermission则是java8新增的工具类,用于管理HttpURLConniction的权限问题,如果不重复造轮子基本用不到,即是要用到也在用到的时候查看文档即可(java的类太多了)。