/**
* @author: 苗士军
* @description URL工具类
*/
UrlUtils = {
/**
* @description 判断url是否存在(存在跨域问题)
* @param _url
* @return {boolean}
*/
isTrueUrl: function (_url) {
result = false;
if (_url == undefined || _url == '') {
return result;
}
$.ajax({
url: _url,
type: "get",
async: false,
success: function () {
result = true;
},
statusCode: {
404: function () {
}
}
});
return result;
},
/**
* @description 解析url
* @param url
* @return {{source: *, protocol, host: string, port: (*|Function|string), query: (*|string), params, file: *, hash, path: string, relative: *, segments: Array}}
*/
parseURL: function (url) {
var a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':', ''),
host: a.hostname,
port: a.port,
query: a.search,
params: (function () {
var ret = {},
seg = a.search.replace(/^\?/, '').split('&'),
len = seg.length, i = 0, s;
for (; i < len; i++) {
if (!seg[i]) {
continue;
}
s = seg[i].split('=');
ret[s[0]] = s[1];
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
hash: a.hash.replace('#', ''),
path: a.pathname.replace(/^([^\/])/, '/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
segments: a.pathname.replace(/^\//, '').split('/')
};
},
/**
* @description 解析url获取参数
* @param path
* @return {{}}
*/
getParam: function (path) {
var result = {}, param = /([^?=&]+)=([^&]+)/ig, match;
while ((match = param.exec(path)) != null) {
result[match[1]] = match[2];
}
return result;
}
}
/**
* URL 工具类
*/
class UrlUtils {
/**
* 检查 URL 是否可访问
* @param {string} url URL 地址
* @param {Object} options 配置选项
* @param {string} options.method 请求方法
* @param {number} options.timeout 超时时间(毫秒)
* @return {Promise<boolean>} 是否可访问
*/
static async isUrlAccessible(url, options = {}) {
if (!url) {
return false;
}
const { method = 'HEAD', timeout = 3000 } = options;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
method,
mode: 'cors',
cache: 'no-cache',
signal: controller.signal
});
clearTimeout(timeoutId);
return response.ok;
} catch (error) {
return false;
}
}
/**
* 解析 URL
* @param {string} url URL 地址
* @return {Object} 解析结果
*/
static parseUrl(url) {
if (!url) {
throw new Error('URL 不能为空');
}
const a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':', ''),
host: a.hostname,
port: a.port || (a.protocol === 'https:' ? '443' : '80'),
query: a.search,
params: this.getParams(url),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
hash: a.hash.replace('#', ''),
path: a.pathname.replace(/^([^\/])/, '/$1'),
relative: (a.href.match(/https?:\/\/[^\/]+(.+)/) || [, ''])[1],
segments: a.pathname.replace(/^\//, '').split('/')
};
}
/**
* 获取 URL 参数
* @param {string} url URL 地址
* @return {Object} 参数对象
*/
static getParams(url) {
if (!url) {
return {};
}
const result = {};
const paramRegex = /([^?=&]+)=([^&]+)/ig;
let match;
while ((match = paramRegex.exec(url)) !== null) {
try {
result[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
} catch (error) {
result[match[1]] = match[2];
}
}
return result;
}
/**
* 构建 URL
* @param {string} baseUrl 基础 URL
* @param {Object} params 参数对象
* @return {string} 构建后的 URL
*/
static buildUrl(baseUrl, params = {}) {
if (!baseUrl) {
throw new Error('基础 URL 不能为空');
}
const url = new URL(baseUrl);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.append(key, value);
}
});
return url.toString();
}
/**
* 向 URL 添加参数
* @param {string} url URL 地址
* @param {Object} params 要添加的参数
* @return {string} 新的 URL
*/
static addParams(url, params = {}) {
if (!url) {
throw new Error('URL 不能为空');
}
const urlObj = new URL(url);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
urlObj.searchParams.set(key, value);
}
});
return urlObj.toString();
}
/**
* 从 URL 中移除参数
* @param {string} url URL 地址
* @param {Array<string>} paramNames 要移除的参数名
* @return {string} 新的 URL
*/
static removeParams(url, paramNames = []) {
if (!url) {
throw new Error('URL 不能为空');
}
const urlObj = new URL(url);
paramNames.forEach(name => {
urlObj.searchParams.delete(name);
});
return urlObj.toString();
}
/**
* 验证 URL 格式
* @param {string} url URL 地址
* @return {boolean} 是否为有效 URL
*/
static isValidUrl(url) {
try {
new URL(url);
return true;
} catch (error) {
return false;
}
}
/**
* 计算相对路径
* @param {string} from 起始 URL
* @param {string} to 目标 URL
* @return {string} 相对路径
*/
static getRelativePath(from, to) {
if (!from || !to) {
throw new Error('起始和目标 URL 不能为空');
}
const fromUrl = new URL(from);
const toUrl = new URL(to);
if (fromUrl.origin !== toUrl.origin) {
return to;
}
const fromPath = fromUrl.pathname.split('/').filter(Boolean);
const toPath = toUrl.pathname.split('/').filter(Boolean);
let commonLength = 0;
while (commonLength < fromPath.length && commonLength < toPath.length &&
fromPath[commonLength] === toPath[commonLength]) {
commonLength++;
}
const relativeParts = Array(fromPath.length - commonLength).fill('..');
relativeParts.push(...toPath.slice(commonLength));
let relativePath = relativeParts.join('/');
if (toUrl.search) {
relativePath += toUrl.search;
}
if (toUrl.hash) {
relativePath += toUrl.hash;
}
return relativePath || '.';
}
}
// 检查 URL 是否可访问
UrlUtils.isUrlAccessible('https://www.example.com')
.then(accessible => console.log('URL 可访问:', accessible));
// 解析 URL
const parsed = UrlUtils.parseUrl('https://www.example.com/path?a=1&b=2#hash');
console.log('解析结果:', parsed);
// 构建 URL
const url = UrlUtils.buildUrl('https://www.example.com', { a: 1, b: 2 });
console.log('构建的 URL:', url);
// 验证 URL
console.log('URL 有效:', UrlUtils.isValidUrl('https://www.example.com'));