该类用于生成一个可用的URL,方便URL的生成和管理。
/**
* 构造的URL
* @author Alvin
*/
final public class UrlBuilder {
// 协议名称
private String scheme;
// 服务器名称
private String serverName;
// 端口号
private int serverPort;
// 上下文名称
private String[] paths;
// 参数集合
private Map<String, Object> parameters;
// 地址缓冲
private String urlBuffer = null;
// 路径缓冲
private String pathBuffer = null;
// 参数缓冲
private String parametersBuffer = null;
/**
* 构造器
*/
public UrlBuilder(String scheme, String serverName, int serverPort, String[] paths) {
if (paths != null) {
for (int i = 0; i < paths.length; i++) {
if (paths[i].isEmpty() == false) {
paths[i] = trimSeparator(paths[i]);
}
}
}
this.scheme = scheme;
this.serverName = serverName;
this.serverPort = serverPort;
this.paths = paths;
}
/**
* 构造器
*/
public UrlBuilder(String serverName, int serverPort, String[] paths) {
this("http", serverName, serverPort, paths);
}
/**
* 构造器
*/
public UrlBuilder(String serverName, int serverPort) {
this(serverName, serverPort, null);
}
/**
* 构造器
*/
public UrlBuilder(String serverName) {
this(serverName, 80, null);
}
/**
* 去除字符串前后的 / 字符和空白字符
*/
private static String trimSeparator(String path) {
int pos = 0, pos2 = path.length() - 1;
// 跳过字符串前端的所有 / 字符和空格字符
for (; pos < path.length() && (path.charAt(pos) == '/' || Character.isSpaceChar(path.charAt(pos))); pos++);
// 跳过字符串末尾的所有 / 字符和空格字符
for (; pos2 > 0 && (path.charAt(pos2) == '/' || Character.isSpaceChar(path.charAt(pos2))); pos2--);
// 判断字符串是否有效
if (pos > pos2) {
throw new IllegalArgumentException(path);
}
return path.substring(pos, pos2 + 1);
}
/**
* 添加一个目录
*/
public void addPath(String path) {
// 令路径缓冲字符串失效
pathBuffer = null;
// 令URL缓冲字符串失效
urlBuffer = null;
// 保存路径字符串
if (paths == null) {
// 将路径字符串保存为数组
paths = new String[] { trimSeparator(path) };
} else {
// 扩展数组并在最后一项添加路径字符串
paths = Arrays.copyOf(paths, paths.length + 1);
paths[paths.length - 1] = trimSeparator(path);
}
}
/**
* 添加若干目录
*/
public void addPath(String...paths) {
// 遍历并去除字符串中的非法字符
for (int i = 0; i < paths.length; i++) {
paths[i] = trimSeparator(paths[i]);
}
// 存储保存路径的数组
if (this.paths == null) {
this.paths = paths;
} else {
int len = this.paths.length;
this.paths = Arrays.copyOf(this.paths, len + paths.length);
for (int i = 0; i < paths.length; i++) {
this.paths[len + i] = paths[i];
}
}
// 令缓冲失效
pathBuffer = null;
urlBuffer = null;
}
/**
* 删除目录
*/
public String removePath(int index) {
String value = paths[index];
// 新建数组比原数组长度少1
String[] newArray = new String[paths.length - 1];
// 将删除位置前的内容拷贝到新数组
System.arraycopy(paths, 0, newArray, 0, index);
// 将删除位置之后的内容拷贝到新数组
System.arraycopy(paths, index + 1, newArray, index, newArray.length - index);
paths = newArray;
urlBuffer = null;
pathBuffer = null;
return value;
}
/**
* 转为字符串
*/
public String toString(String encoding) {
// 判断缓冲是否有效
if (urlBuffer == null) {
StringBuilder s = new StringBuilder(scheme); // 写入协议名
s.append("://"); // 分隔符
s.append(serverName); // 写入服务器名
s.append(serverPort == 80 ? "" : ":" + serverPort); // 写入端口号
if (isPathExsit()) {
s.append("/" + getPath()); // 写入路径
}
// 写入参数字符串
if (isParametersExsit()) {
s.append("?");
s.append(getParamString(encoding));
}
urlBuffer = s.toString();
}
return urlBuffer;
}
@Override
public String toString() {
return toString("UTF-8");
}
/**
* 添加一个参数
*/
public void addParameter(String name, Object value) {
if (parameters == null) {
parameters = new LinkedHashMap<>();
}
parameters.put(name, value);
parametersBuffer = null;
}
/**
* 添加一系列参数
*/
public void addParametersMap(Map<String, Object> parameters) {
this.parameters = parameters;
parametersBuffer = null;
}
/**
* 删除一个参数
*/
public Object removeParameter(String name) {
if (parameters == null) {
return null;
}
parametersBuffer = null;
return parameters.remove(name);
}
/**
* 获取一个参数
*/
public Object getParameter(String name) {
if (parameters == null) {
return null;
}
return parameters.get(name);
}
/**
* 删除所有参数
*/
public void clearParameters() {
if (parameters != null) {
parameters.clear();
parametersBuffer = null;
}
}
/**
* 设置协议名称
*/
public void setScheme(String scheme) {
this.scheme = scheme;
urlBuffer = null;
}
/**
* 获取协议名称
*/
public String getScheme() {
return scheme;
}
/**
* 设置服务器名称
*/
public void setServerName(String serverName) {
this.serverName = serverName;
urlBuffer = null;
}
/**
* 获取服务器名称
*/
public String getServerName() {
return serverName;
}
/**
* 设置服务器端口号
*/
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
urlBuffer = null;
}
/**
* 获取服务器端口号
*/
public int getServerPort() {
return serverPort;
}
/**
* 获取服务器第n级路径
*/
public String getPath(int index) {
return paths[index];
}
/**
* 获取服务器路径长度
* @return
*/
public int getPathCount() {
return paths == null ? 0 : paths.length;
}
/**
* 网址中是否存在路径
*/
public boolean isPathExsit() {
return paths != null;
}
/**
* 获取服务器路径字符串
*/
public String getPath() {
if (paths == null) {
return "";
}
if (pathBuffer == null) {
StringBuilder s = new StringBuilder();
char spliter = 0;
for (String path : paths) {
if (spliter == 0) {
spliter = '/';
} else {
s.append(spliter);
}
s.append(path);
}
pathBuffer = s.toString();
}
return pathBuffer;
}
/**
* 是否具有参数
*/
public boolean isParametersExsit() {
return ObjectHelper.isMapNullOrEmpty(parameters) == false;
}
/**
* 获取参数字符串
*/
public String getParamString(String encoding) {
if (parametersBuffer == null) {
parametersBuffer = makeParameters(parameters, encoding);
}
return parametersBuffer;
}
/**
* 获取参数字符串
*/
public String getParamString() {
return getParamString("UTF-8");
}
/**
* 获取网站根
*/
public String getBaseURL() {
StringBuilder b = new StringBuilder(scheme);
b.append("://")
.append(serverName)
.append(serverPort == 80 ? "" : ":" + serverPort)
.append("/");
return b.toString();
}
/**
* 解析URL
*/
public static UrlBuilder parse(String url, String urlEncoding) {
String schema, serverName, path = null, queryString = null;
int pos, pos2, port;
// 查找协议分隔符位置
pos = url.indexOf("://");
if (pos < 0) { // 查找失败
// 设定默认协议
schema = "http";
// 重置查找起始点
pos = 0;
} else {
// 获取协议名称
schema = url.substring(0, pos).trim();
if (schema.isEmpty()) {
throw new IllegalArgumentException(url);
}
// 设置查找起始点
pos += 3;
}
// 查找随后的 / 字符位置
pos2 = url.indexOf("/", pos);
if (pos2 < 0) { // 查找失败
// 查找?所在位置
pos2 = url.indexOf("?", pos);
if (pos2 < 0) {
// 令字符串剩余部分作为服务器名称
serverName = url.substring(pos).trim();
} else {
// 将?之前的部分作为服务器名称
serverName = url.substring(pos, pos2).trim();
queryString = url.substring(pos2 + 1).trim();
}
} else {
// 获取服务器名称
serverName = url.substring(pos, pos2).trim();
}
if (serverName.isEmpty()) {
throw new IllegalArgumentException(url);
}
// 设置查找起始点
pos = pos2 + 1;
// 在服务器名称中查找 : 字符的位置
pos2 = serverName.lastIndexOf(":");
if (pos2 < 0) { // 查找失败
// 设定默认端口号
port = 80;
} else {
// 获取端口号
port = Integer.parseInt(serverName.substring(pos2 + 1));
// 重新设定服务器名称(去除端口号部分)
serverName = serverName.substring(0, pos2);
}
// 判断解析是否结束
if (queryString == null && pos > 0) {
// 查找?字符出现的位置
pos2 = url.indexOf("?", pos);
if (pos2 < 0) { // 查找失败
// 获取路径字符串
path = url.substring(pos);
} else {
// 获取路径字符串
path = url.substring(pos, pos2);
// 获取参数字符串
queryString = url.substring(pos2 + 1);
}
}
// 生成对象实例
UrlBuilder builder = new UrlBuilder(schema, serverName, port, path == null ? null : path.split("/"));
// 解析参数字符串
if (queryString != null) {
// 获取参数数组
String[] qs = queryString.split("&");
if (qs != null) {
builder.parameters = new LinkedHashMap<>();
// 遍历参数数组
for (String q : qs) {
// 获取参数名称和参数值
String[] nv = q.split("=", 2);
if (nv != null) {
try {
// 设置参数
builder.parameters.put(nv[0], nv.length > 1 ? URLDecoder.decode(nv[1], urlEncoding) : "");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(url);
}
}
}
}
}
return builder;
}
/**
* 解析URL
*/
public static UrlBuilder parse(String url) {
return parse(url, "UTF-8");
}
/**
* 产生参数字符串
*/
public static String makeParameters(Map<String, Object> parameters, String encoding) {
// 判断参数集合是否有效
if (ObjectHelper.isMapNullOrEmpty(parameters)) {
return "";
}
StringBuilder s = new StringBuilder();
char spliter = 0;
// 遍历参数集合
for (Entry<String, Object> e : parameters.entrySet()) {
// 写入参数分隔符
if (spliter == 0) {
spliter = '&';
} else {
s.append(spliter);
}
// 写入参数名
s.append(e.getKey());
s.append("=");
try {
// 写入参数值
s.append(e.getValue() == null ? "" : URLEncoder.encode(String.valueOf(e.getValue()), encoding));
} catch (UnsupportedEncodingException e1) {
throw new IllegalArgumentException(encoding);
}
}
// 返回参数字符串
return s.toString();
}
/**
* 产生参数字符串
*/
public static String makeParameters(Map<String, Object> parameters) {
return makeParameters(parameters, "UTF-8");
}
/**
* 连接两个地址
*/
public static String combinUrl(String url1, String url2) {
boolean b1 = url1.endsWith("/"), b2 = url2.startsWith("/");
// 判断地址1是否以 / 结尾,且地址2是否以 / 开头
if (b1 && b2) {
// 保留一个 / 字符
return url1 + url2.substring(1);
}
// 判断地址1是否以 / 结尾,或者地址2是否以 / 开头
if (b1 || b2) {
// 直接连接两个地址
return url1 + url2;
}
// 在两个地址间加上 / 字符后连接
return url1 + "/" + url2;
}
}
测试代码如下:
public class HttpTestUnit {
@Test
public void test01_组织URL() {
UrlBuilder builder = new UrlBuilder("localhost", 8080);
builder.addPath("xxx");
System.out.println(builder.toString());
System.out.println("---------------");
builder.addPath("aaa", "bbb");
System.out.println(builder.toString());
System.out.println("---------------");
builder.removePath(1);
System.out.println(builder.toString());
System.out.println("---------------");
builder.addParameter("id", 100);
System.out.println(builder.toString());
System.out.println("---------------");
builder.addParameter("name", "小李");
System.out.println(builder.toString());
System.out.println("---------------");
builder.removeParameter("id");
System.out.println(builder.toString());
System.out.println("---------------");
builder = UrlBuilder.parse("http://www.baidu.com:8080/");
System.out.println(builder);
System.out.println("scheme=" + builder.getScheme());
System.out.println("server=" + builder.getServerName());
System.out.println("port=" + builder.getServerPort());
System.out.println("---------------");
builder = UrlBuilder.parse("ftp://www.baidu.com/a/b/c.jsp");
System.out.println(builder);
System.out.println("scheme=" + builder.getScheme());
System.out.println("server=" + builder.getServerName());
System.out.println("port=" + builder.getServerPort());
for (int i = 0; i < builder.getPathCount(); i++) {
System.out.println("path" + (i + 1) + "=" + builder.getPath(i));
}
System.out.println("---------------");
builder = UrlBuilder.parse("www.baidu.com/a/b/c.jsp?id=1");
System.out.println(builder);
System.out.println("scheme=" + builder.getScheme());
System.out.println("server=" + builder.getServerName());
System.out.println("port=" + builder.getServerPort());
for (int i = 0; i < builder.getPathCount(); i++) {
System.out.println("path" + (i + 1) + "=" + builder.getPath(i));
}
System.out.println("id=" + builder.getParameter("id"));
builder.removePath(1);
System.out.println(builder);
System.out.println("---------------");
builder = UrlBuilder.parse("www.baidu.com?id=1");
System.out.println(builder);
System.out.println("scheme=" + builder.getScheme());
System.out.println("server=" + builder.getServerName());
System.out.println("port=" + builder.getServerPort());
System.out.println("id=" + builder.getParameter("id"));
}
}