Java为网络支持提供了java.net包,该包下的URL和URLConnection等类提供了以编程方式访问Web服务的功能,而URLDecoder和URLEncoder则提供了普通字符串和application/x-www-form-urlencoded MIME字符串相互转化的静态方法。
InetAddress
Java提供了InetAddress类来代表IP地址,InetAddress 下还有两个子类:Inet4Address、 Inet6Address,它们分别代表Internet Protocol version4(IPv4)地址和 Internet Protocol version6(IPv6)地址。
InetAddress类没有提供构造器,而是提供了如下两个静态方法来获取InetAddress 实例。
- getByName(String host):根据主机获取对应的InetAddress 对象。
- getByAddress(byte[] addr):根据原始IP地址 来 获取对应的InetAddress 对象。
InetAddress还提供了如下三个方法来获取InetAddress 实例对应的IP地址和主机名。
- String getCanonicalHostName():获取此IP地址的全限定域名 。
- String getHostAddress():返回该InetAddress 实例对应的IP地址字符串(以字符串形式)。
- String getHostName():获取此IP地址的主机名 。
除此之外,InetAddress还提供了一个isReachable()方法,用于测试是否可以到达该地址。该方法将尽最大努力试图到达主机,但防火墙和服务器配置可能阻塞请求,使得它在访问某些特定的端口时处于不可达状态。如果可以获得权限,典型的实现将使用ICMP ECHO REQUEST;否则它将试图在目标主机的端口7(Echo)上建立TCP连接。
public static void InetAddressTest(){
try {
// 根据主机名来获取对应的InerAddress实例
InetAddress ip = InetAddress.getByName("www.baidu.com");
// 判断是否可达
System.out.println("baidu是否可达:" + ip.isReachable(2000));
// 获取该InetAddress实例的IP字符串
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
// 根据原始IP地址来获取对应的InetAddress实例
InetAddress local = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
System.out.println("本机是否可达:" + local.isReachable(5000));
// 获取该InetAddress实例对应的全限定域名
System.out.println(local.getCanonicalHostName());
} catch (Exception e){
e.printStackTrace();
}
}
使用URLDecoder和URLEncoder
URLDecoder和URLEncoder用于完成普通字符串和application/x-www-form-urlencoded MIME字符串之间的相互转化。
当URL地址里包含非西欧字符的字符串时,系统会将这些非西欧字符串转换成特定的字符串,也就是我们常说的“乱码”,即 application/x-www-form-urlencoded MIME字符串。
编程过程中可能涉及普通字符串和这种特殊字符串的相关转换,这就需要使用 URLDecoder和URLEncoder类。
- URLDecoder类包含一个decode(String s, String enc)静态方法,它可以将看上去是乱码的特殊字符串转换成普通字符串。
- URLEncoder 类包含一个encode(String s, String enc)静态方法,它可以将普通字符串 转换成 application/x-www-form-urlencoded MIME字符串
public static void URLDecoderTest() {
try{
// 将application/x-www-form-urlencoded MIME字符串
// 转换成普通字符串
// 其中的字符串直接从浏览器复制过来
String url = "https://blog.csdn.net/gqg_guan/article/details/127994896?spm=1001.2014.3001.5502";
String keyWord = URLDecoder.decode(url, "utf-8");
System.out.println(keyWord);
// 将普通字符串转成
// application/x-www-form-urlencoded MIME字符串
String str = "百度一下,你就知道";
String urlStr = URLEncoder.encode(str, "GBK");
System.out.println(urlStr);
}catch (Exception e){
e.printStackTrace();
}
}
URL、URLConnection和URLPermission
URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂对象的引用,例如对数据库或搜索引擎的查询。在通常情况下,URL可以由协议名、主机、端口和资源组成,即满足如下格式:
protocol:///host:port/resourceName
例如如下的URL地址:
http://www.baidu.com
提示:
JDK中还提供了一个URI(Uniform Resource Identifiers)类,其实例代表一个统一资源标识符,Java的URI不能用于定位任何资源,它的唯一作用就是解析。与此对应的是,URL则包含一个可打开到达该资源的输入流,可以将URL理解成URI的特例。
URL类提供了多个构造器用于创建URL对象,一旦获得了URL对象之后,就可以调用如下方法来访问该URL对应的资源。
- String getFile():获取该URL的资源名。
- String getHost():获取该 URL的主机名。
- String getPath():获取该 URL的路径部分。
- int getPort():获取该 URL的端口号。
- String getProtocol():获取该 URL的协议名称。
- String getQuery():获取该 URL的查询字符串部分。
- URLConnection openConnection():返回一个 URLConnection对象,它代表了与URL所引用的远程对象的连接。
- InputStream openStream():打开与此URL的连接,并返回一个用于读取该URL资源的 InputStream。
实例:实现一个多线程下载工具类
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.*;
public class DownUtil {
//定义下载资源的路径
private String path;
//指定所下载文件的保存位置
private String targetFile;
//定义需要使用多少个线程下载资源
private int threadNum;
//定义下载的线程对象
private DownThread[] threads;
//定义下载的文件的总大小
private int fileSize;
public DownUtil(String path, String targetFile, int threadNum) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
//初始化Thread数组
threads=new DownThread[threadNum];
}
public void download() throws IOException {
URL url = new URL(path);
//获取连接对象
HttpURLConnection conn =(HttpURLConnection) url.openConnection();
//得到文件大小
fileSize= conn.getContentLength();
conn.disconnect();
//平均分给每个线程多少字节,如果除不尽给每个线程多读一个字节
int currentPartSize=fileSize/threadNum+1;
RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
//设置本地文件大小
System.out.println(fileSize);
file.close();
for (int i = 0; i < threadNum; i++) {
//计算每个线程下载的开始位置
int startPos=i*currentPartSize;
System.out.println(startPos);
//每个线程使用一个RandomAccessFile(进行下载
RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
//定位该线程的下载位置
currentPart.seek(startPos);
//创建下载线程
threads[i] = new DownThread(startPos, currentPartSize, currentPart);
threads[i].start();
}
}
//获取下载的百分比
public double getCompleteRate(){
//统计多个线程已经下载的总大小
int sumSize=0;
for (int i = 0; i < threadNum; i++) {
sumSize+=threads[i].length;
}
//返回已经完成的百分比
return sumSize*1.0/fileSize;
}
private class DownThread extends Thread{
//当前线程的下载位置
private int startPos;
//定义当前线程负责下载的文件大小
private int currentPartSize;
//当前线程需要下载的文件块
private RandomAccessFile currentPart;
//该线程已下载的字节数
public int length;
public DownThread(int startPos, int currentPartSize, RandomAccessFile currentPart) {
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currentPart = currentPart;
}
@Override
public void run() {
try {
handle();
} catch (Exception e) {
e.printStackTrace();
}
}
private void handle() throws IOException {
URL url = new URL(path);
//获取连接对象
HttpURLConnection conn =(HttpURLConnection) url.openConnection();
InputStream in = conn.getInputStream();
//跳过startPos个字节
in.skip(this.startPos);
currentPart.seek(this.startPos);
byte[] buff = new byte[1024 * 8];
int hasRead=0;
while (length<currentPartSize&&(hasRead=in.read(buff))>0){
currentPart.write(buff,0,hasRead);
//累计下载总大小
length+=hasRead;
}
currentPart.close();
in.close();
}
}
}
测试
import java.io.IOException;
public class MultThreadDown {
public static void main(String[] args) throws IOException {
DownUtil downUtil = new DownUtil("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fask.qcloudimg.com%2Fhttp-save%2Fdeveloper-news%2F8dptjnxytv.gif&refer=http%3A%2F%2Fask.qcloudimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671772288&t=e07c38573c59898a73ebf0a978ea838b"
, "test.jpg", 4);
//开始下载
downUtil.download();
System.out.println("hello");
new Thread(()->{
while (downUtil.getCompleteRate()<1){
System.out.println("已完成:"+downUtil.getCompleteRate());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//每隔0.1秒查询一次任务完成的进度
}).start();
}
}
如果要断点赋值则需要一个配置文件来存放每个线程每次断点时正在读取的位置,以便下一次从此位置开始读取。