使用JavaScript将相对路径转换为绝对路径


71

有一个功能,可以给我类似以下网址:

./some.css
./extra/some.css
../../lib/slider/slider.css

它总是一条相对路径。

假设我们知道页面的当前路径,例如http://site.com/stats/2012/,不确定如何将这些相对路径转换为真实路径?

我们应该得到这样的东西:

./some.css => http://site.com/stats/2012/some.css
./extra/some.css => http://site.com/stats/2012/extra/some.css
../../lib/slider/slider.css => http://site.com/lib/slider/slider.css

没有jQuery,只有原始javascript。


7
这是针对javascript应用程序,而不是常规站点。
贾斯珀

Answers:


45

应该这样做:

function absolute(base, relative) {
    var stack = base.split("/"),
        parts = relative.split("/");
    stack.pop(); // remove current file name (or empty string)
                 // (omit if "base" is the current folder without trailing slash)
    for (var i=0; i<parts.length; i++) {
        if (parts[i] == ".")
            continue;
        if (parts[i] == "..")
            stack.pop();
        else
            stack.push(parts[i]);
    }
    return stack.join("/");
}

前几天~,有些网站拥有内部资源,,~/media/style.css
丹尼尔·利兹克

@Daniel_L:~通常表示以外的含义base。这也不是相对路径:-)如果您需要包含代字号的路径的帮助,请询问一个新问题
Bergi

2
对于以后遇到此问题的任何人,使用document.location.href作为base对我来说都是有效的。
nixkuroi

2
当相对网址/以例如开头时,此解决方案会出现问题/some.css。在这种情况下,正确的实现方式将删除域名之后的堆栈中的所有项目。
Vineet

1
@Vineet正确,但是以开头的路径/不是相对的。我的函数仅考虑路径,而不考虑URI。
Bergi '17

120

最简单,有效和正确的方法是仅使用URL api。

new URL("http://www.stackoverflow.com?q=hello").href;
//=> http://www.stackoverflow.com/?q=hello"

new URL("mypath","http://www.stackoverflow.com").href;
//=> "http://www.stackoverflow.com/mypath"

new URL("../mypath","http://www.stackoverflow.com/search").href
//=> "http://www.stackoverflow.com/mypath"

new URL("../mypath", document.baseURI).href
//=> "/programming/mypath"

在性能方面,此解决方案与使用字符串操作相当,并且是创建a标签的速度的两倍。


2
喜欢这个解决方案,但是由于缺少URL支持或它不能与React Native一起使用。相反,我使用了https://www.npmjs.com/package/url,效果很好。
jsaven

3
@hex您是对的,使用window.location.href具有此缺点。我已经编辑了答案以使其document.baseURI代替使用。
Flimm

这将不是针对开发环境的通用解决方案。
SuperEye

如何在ubuntu中运行此解决方案?
vqlk

68

Javascript将为您完成。无需创建函数。

var link = document.createElement("a");
link.href = "../../lib/slider/slider.css";
alert(link.protocol+"//"+link.host+link.pathname+link.search+link.hash);

// Output will be "http://www.yoursite.com/lib/slider/slider.css"

但是,如果需要将其用作功能:

var absolutePath = function(href) {
    var link = document.createElement("a");
    link.href = href;
    return (link.protocol+"//"+link.host+link.pathname+link.search+link.hash);
}

更新:如果需要完整的绝对路径,则为简单版本:

var absolutePath = function(href) {
    var link = document.createElement("a");
    link.href = href;
    return link.href;
}

4
这在IE中不起作用,甚至在IE11中也不起作用。详情请参见:stackoverflow.com/questions/470832/…–
奥兰·丹尼森

3
效果很好(在IE中也是如此)...我发现link.href设置后简单地访问就返回了已解析的URL(即,无需手动重建href)。
克里斯·巴克斯特

@ChrisBaxter同样在这里。我很生气,这个流行的答案是指协议,主机等...而href看起来更简单,更安全。是否有一个原因?我想知道...
philippe_b

它实际上是DOM解决方案
Lukasz Prus

6

来自MDN的这是坚不可摧的!

/*\
|*|
|*|  :: translate relative paths to absolute paths ::
|*|
|*|  https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|*|
|*|  The following code is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
\*/

function relPathToAbs (sRelPath) {
  var nUpLn, sDir = "", sPath = location.pathname.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, "$1"));
  for (var nEnd, nStart = 0; nEnd = sPath.indexOf("/../", nStart), nEnd > -1; nStart = nEnd + nUpLn) {
    nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
    sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp("(?:\\\/+[^\\\/]*){0," + ((nUpLn - 1) / 3) + "}$"), "/");
  }
  return sDir + sPath.substr(nStart);
}

用法示例:

/* Let us be in /en-US/docs/Web/API/document.cookie */

alert(location.pathname);
// displays: /en-US/docs/Web/API/document.cookie

alert(relPathToAbs("./"));
// displays: /en-US/docs/Web/API/

alert(relPathToAbs("../Guide/API/DOM/Storage"));
// displays: /en-US/docs/Web/Guide/API/DOM/Storage

alert(relPathToAbs("../../Firefox"));
// displays: /en-US/docs/Firefox

alert(relPathToAbs("../Guide/././API/../../../Firefox"));
// displays: /en-US/docs/Firefox

2
它不使用领先的处理相对路径/。示例:relPathToAbs("/?foo=bar")应返回“ /?foo = bar”,而不是返回/questions/14780350/?foo=bar,这表示/在将路径发送回根目录时未正确检测到前导。
Nucleon

@Nucleon路径是包含在协议段(http(s)://)和搜索/哈希段(?/ #)之间的URL的一部分。不用担心将函数的路径部分与搜索/哈希部分分开,这也是因为正则表达式可以使它变得非常简单:relPathToAbs("./?foo=bar#someHash".replace(/^[^\?#]*/, "$&"))
madmurphy 2014年

1
@Nucleon对不起,我误会了您的评论。一个路径开始/已经一个绝对路径!因此,将其作为该函数的参数没有意义!该字符串"/?foo=bar" 已经是绝对路径。
madmurphy 2014年

确实如此,但是请在此页面的URL上运行函数relPathToAbs("/?foo=bar")。它不会返回/?foo=bar(因为它已经是绝对的),所以它会返回/questions/14780350/?foo=bar
Nucleon 2014年

1
@Nucleon函数期望您给出相对路径。字符串/?foo=bar错误的相对路径(实际上是绝对路径)。有效的相对路径必须以开头/^(?:\.\.?\/)*[^\/]/。例如:/^(?:\.\.?\/)*[^\/]/.test("./hello/world")-> true; /^(?:\.\.?\/)*[^\/]/.test("../hi")-> true; /^(?:\.\.?\/)*[^\/]/.test("../././../foo")-> true; /^(?:\.\.?\/)*[^\/]/.test("/")-> false; /^(?:\.\.?\/)*[^\/]/.test("/?foo=bar")-> false;
madmurphy 2014年

6

如果要对浏览器中的自定义网页(而不是运行脚本的页面)中的链接进行相对绝对转换,则可以使用@Bergi建议的功能的增强版本:

var resolveURL=function resolve(url, base){
    if('string'!==typeof url || !url){
        return null; // wrong or empty url
    }
    else if(url.match(/^[a-z]+\:\/\//i)){ 
        return url; // url is absolute already 
    }
    else if(url.match(/^\/\//)){ 
        return 'http:'+url; // url is absolute already 
    }
    else if(url.match(/^[a-z]+\:/i)){ 
        return url; // data URI, mailto:, tel:, etc.
    }
    else if('string'!==typeof base){
        var a=document.createElement('a'); 
        a.href=url; // try to resolve url without base  
        if(!a.pathname){ 
            return null; // url not valid 
        }
        return 'http://'+url;
    }
    else{ 
        base=resolve(base); // check base
        if(base===null){
            return null; // wrong base
        }
    }
    var a=document.createElement('a'); 
    a.href=base;

    if(url[0]==='/'){ 
        base=[]; // rooted path
    }
    else{ 
        base=a.pathname.split('/'); // relative path
        base.pop(); 
    }
    url=url.split('/');
    for(var i=0; i<url.length; ++i){
        if(url[i]==='.'){ // current directory
            continue;
        }
        if(url[i]==='..'){ // parent directory
            if('undefined'===typeof base.pop() || base.length===0){ 
                return null; // wrong url accessing non-existing parent directories
            }
        }
        else{ // child directory
            base.push(url[i]); 
        }
    }
    return a.protocol+'//'+a.hostname+base.join('/');
}

null如果出现问题,它将返回。

用法:

resolveURL('./some.css', 'http://example.com/stats/2012/'); 
// returns http://example.com/stats/2012/some.css

resolveURL('extra/some.css', 'http://example.com/stats/2012/');
// returns http://example.com/stats/2012/extra/some.css

resolveURL('../../lib/slider/slider.css', 'http://example.com/stats/2012/');
// returns http://example.com/lib/slider/slider.css

resolveURL('/rootFolder/some.css', 'https://example.com/stats/2012/');
// returns https://example.com/rootFolder/some.css

resolveURL('localhost');
// returns http://localhost

resolveURL('../non_existing_file', 'example.com')
// returns null

2
function canonicalize(url) {
    var div = document.createElement('div');
    div.innerHTML = "<a></a>";
    div.firstChild.href = url; // Ensures that the href is properly escaped
    div.innerHTML = div.innerHTML; // Run the current innerHTML back through the parser
    return div.firstChild.href;
}

与其他解决方案不同,这也适用于IE6(请参阅从相对的解决方案获取绝对URL。(IE6问题)


为什么要“通过解析器运行当前的innerHTML”?
2013年

检查链接在这里的博客文章:stackoverflow.com/a/22918332/82609
Sebastien Lorber

2

提议并接受的解决方案不支持服务器相对URL,并且不适用于绝对URL。例如,如果我的亲戚是/ sites / folder1,它将无法正常工作。

这是另一个功能,它支持完整的服务器相对或相对URL以及上一级的../。它不是完美的,但是涵盖了很多选项。当您的基本URL不是当前页面URL时,请使用此选项,否则会有更好的选择。

    function relativeToAbsolute(base, relative) {
    //make sure base ends with /
    if (base[base.length - 1] != '/')
        base += '/';

    //base: https://server/relative/subfolder/
    //url: https://server
    let url = base.substr(0, base.indexOf('/', base.indexOf('//') + 2));
    //baseServerRelative: /relative/subfolder/
    let baseServerRelative = base.substr(base.indexOf('/', base.indexOf('//') + 2));
    if (relative.indexOf('/') === 0)//relative is server relative
        url += relative;
    else if (relative.indexOf("://") > 0)//relative is a full url, ignore base.
        url = relative;
    else {
        while (relative.indexOf('../') === 0) {
            //remove ../ from relative
            relative = relative.substring(3);
            //remove one part from baseServerRelative. /relative/subfolder/ -> /relative/
            if (baseServerRelative !== '/') {
                let lastPartIndex = baseServerRelative.lastIndexOf('/', baseServerRelative.length - 2);
                baseServerRelative = baseServerRelative.substring(0, lastPartIndex + 1);
            }
        }
        url += baseServerRelative + relative;//relative is a relative to base.
    }

    return url;
}

希望这可以帮助。不能在JavaScript中使用此基本实用程序实在令人沮丧。


据我所知,这不适用于与协议无关的网址吗?例如。//www.example.com/page
利亚姆·格雷

不,但是只要更改以下语句即可轻松支持:else if(relative.indexOf(“://”)> 0)并删除:,但我保持这种方式,因为它可以从基础中获取协议,无论如何我都会动态发送。
Shai Petel


0

我必须在接受的解决方案中添加一个修复程序,因为在angularjs导航中的#号后面可以有斜线。

function getAbsoluteUrl(base, relative) {
  // remove everything after #
  var hashPosition = base.indexOf('#');
  if (hashPosition > 0){
    base = base.slice(0, hashPosition);
  }

  // the rest of the function is taken from http://stackoverflow.com/a/14780463
  // http://stackoverflow.com/a/25833886 - this doesn't work in cordova
  // http://stackoverflow.com/a/14781678 - this doesn't work in cordova
  var stack = base.split("/"),
      parts = relative.split("/");
  stack.pop(); // remove current file name (or empty string)
               // (omit if "base" is the current folder without trailing slash)
  for (var i=0; i<parts.length; i++) {
    if (parts[i] == ".")
      continue;
    if (parts[i] == "..")
      stack.pop();
    else
      stack.push(parts[i]);
  }
  return stack.join("/");
}

我添加if (charAt(0) !== ".") return relative;了处理relative已经是绝对路径的情况,因此在调用此函数之前,不必先检查它是否是相对路径。
mindplay.dk

0

我找到了一个非常简单的解决方案,同时仍可以通过使用历史记录API(IE 10或更高版本)来支持IE 10(IE不支持URL-API )。此解决方案无需任何字符串操作即可工作。

function resolveUrl(relativePath) {
    var originalUrl = document.location.href;
    history.replaceState(history.state, '', relativePath);
    var resolvedUrl = document.location.href;
    history.replaceState(history.state, '', originalUrl);
    return resolvedUrl;
}

history.replaceState()不会触发浏览器导航,但仍会修改document.location并支持相对以及绝对路径。

该解决方案的一个缺点是,如果您已经在使用History-API并设置了带有标题的自定义状态,则当前状态的标题会丢失。


0

这会起作用。但仅当您打开带有文件名的页面时。当您打开这样的链接时,它将无法正常工作stackoverflow.com/page。它将与stackoverflow.com/page/index.php

function reltoabs(link){
    let absLink = location.href.split("/");
    let relLink = link;
    let slashesNum = link.match(/[.]{2}\//g) ? link.match(/[.]{2}\//g).length : 0;
    for(let i = 0; i < slashesNum + 1; i++){
        relLink = relLink.replace("../", "");
        absLink.pop();
    }
    absLink = absLink.join("/");
    absLink += "/" + relLink;
    return absLink;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.