如何在Java中从两个绝对路径(或URL)构造相对路径?

如何在Java中从两个绝对路径(或URL)构造相对路径?,java,url,file,path,Java,Url,File,Path,给定两条绝对路径,例如 /var/data/stuff/xyz.dat /var/data 如何创建以第二条路径为基础的相对路径?在上面的示例中,如果您知道第二个字符串是第一个字符串的一部分,那么结果应该是:/stuff/xyz.dat: String s1 = "/var/data/stuff/xyz.dat"; String s2 = "/var/data"; String s3 = s1.substring(s2.length()); 或者,如果您确实希望在开始时使用句点,如您的示例所

给定两条绝对路径,例如

/var/data/stuff/xyz.dat
/var/data

如何创建以第二条路径为基础的相对路径?在上面的示例中,如果您知道第二个字符串是第一个字符串的一部分,那么结果应该是:
/stuff/xyz.dat

String s1 = "/var/data/stuff/xyz.dat";
String s2 = "/var/data";
String s3 = s1.substring(s2.length());
或者,如果您确实希望在开始时使用句点,如您的示例所示:

String s3 = ".".concat(s1.substring(s2.length()));
Psuedo代码:

  • 使用路径分隔符(“/”)拆分字符串
  • 通过迭代分割字符串的结果来找到最大的公共路径(因此在两个示例中,您最终会得到“/var/data”或“/a”)
  • return.“+whicheverPathIsLonger.substring(commonPath.length)

  • 我假设您有fromPath(文件夹的绝对路径)和toPath(文件夹/文件的绝对路径),并且您正在查找一个路径,该路径将toPath中的文件/文件夹表示为fromPath的相对路径(您当前的工作目录是fromPath),那么类似的内容应该可以工作:

    public static String getRelativePath(String fromPath, String toPath) {
    
      // This weirdness is because a separator of '/' messes with String.split()
      String regexCharacter = File.separator;
      if (File.separatorChar == '\\') {
        regexCharacter = "\\\\";
      }
    
      String[] fromSplit = fromPath.split(regexCharacter);
      String[] toSplit = toPath.split(regexCharacter);
    
      // Find the common path
      int common = 0;
      while (fromSplit[common].equals(toSplit[common])) {
        common++;
      }
    
      StringBuffer result = new StringBuffer(".");
    
      // Work your way up the FROM path to common ground
      for (int i = common; i < fromSplit.length; i++) {
        result.append(File.separatorChar).append("..");
      }
    
      // Work your way down the TO path
      for (int i = common; i < toSplit.length; i++) {
        result.append(File.separatorChar).append(toSplit[i]);
      }
    
      return result.toString();
    }
    
    公共静态字符串getRelativePath(字符串fromPath,字符串toPath){
    //这种奇怪是因为“/”分隔符与String.split()混淆了
    字符串regexCharacter=File.separator;
    如果(File.separatorChar=='\\'){
    regexCharacter=“\\\\”;
    }
    字符串[]fromSplit=fromPath.split(regexCharacter);
    字符串[]toSplit=toPath.split(regexCharacter);
    //找到共同的道路
    int公共=0;
    while(fromSplit[common].equals(toSplit[common])){
    通用++;
    }
    StringBuffer结果=新的StringBuffer(“.”);
    //从这条路走到共同点
    for(int i=common;i
    实际上,如果目标路径不是基本路径的子路径,我的另一个答案就不起作用

    这应该行得通

    public class RelativePathFinder {
    
        public static String getRelativePath(String targetPath, String basePath, 
           String pathSeparator) {
    
            // find common path
            String[] target = targetPath.split(pathSeparator);
            String[] base = basePath.split(pathSeparator);
    
            String common = "";
            int commonIndex = 0;
            for (int i = 0; i < target.length && i < base.length; i++) {
    
                if (target[i].equals(base[i])) {
                    common += target[i] + pathSeparator;
                    commonIndex++;
                }
            }
    
    
            String relative = "";
            // is the target a child directory of the base directory?
            // i.e., target = /a/b/c/d, base = /a/b/
            if (commonIndex == base.length) {
                relative = "." + pathSeparator + targetPath.substring(common.length());
            }
            else {
                // determine how many directories we have to backtrack
                for (int i = 1; i <= commonIndex; i++) {
                    relative += ".." + pathSeparator;
                }
                relative += targetPath.substring(common.length());
            }
    
            return relative;
        }
    
        public static String getRelativePath(String targetPath, String basePath) {
            return getRelativePath(targetPath, basePath, File.pathSeparator);
        }
    }
    

    这有点迂回,但为什么不使用URI呢?它有一个relativize方法,可以为您执行所有必要的检查

    String path = "/var/data/stuff/xyz.dat";
    String base = "/var/data";
    String relative = new File(base).toURI().relativize(new File(path).toURI()).getPath();
    // relative == "stuff/xyz.dat"
    

    请注意,对于文件路径,从java 1.7开始就有
    java.nio.file.path#relativize
    ,正如in.

    所指出的,在使用java.net.URI.relativize时,您应该注意java错误:

    目前,
    URI
    relativize()
    方法只会在一个URI是另一个URI的前缀时相对化URI


    这本质上意味着
    java.net.URI.relativize
    不会为您创建“.”。

    我的版本大致基于和的版本:

    String path = "/var/data/stuff/xyz.dat";
    String base = "/var/data";
    String relative = new File(base).toURI().relativize(new File(path).toURI()).getPath();
    // relative == "stuff/xyz.dat"
    
    /**
     * Returns the path of one File relative to another.
     *
     * @param target the target directory
     * @param base the base directory
     * @return target's path relative to the base directory
     * @throws IOException if an error occurs while resolving the files' canonical names
     */
     public static File getRelativeFile(File target, File base) throws IOException
     {
       String[] baseComponents = base.getCanonicalPath().split(Pattern.quote(File.separator));
       String[] targetComponents = target.getCanonicalPath().split(Pattern.quote(File.separator));
    
       // skip common components
       int index = 0;
       for (; index < targetComponents.length && index < baseComponents.length; ++index)
       {
         if (!targetComponents[index].equals(baseComponents[index]))
           break;
       }
    
       StringBuilder result = new StringBuilder();
       if (index != baseComponents.length)
       {
         // backtrack to base directory
         for (int i = index; i < baseComponents.length; ++i)
           result.append(".." + File.separator);
       }
       for (; index < targetComponents.length; ++index)
         result.append(targetComponents[index] + File.separator);
       if (!target.getPath().endsWith("/") && !target.getPath().endsWith("\\"))
       {
         // remove final path separator
         result.delete(result.length() - File.separator.length(), result.length());
       }
       return new File(result.toString());
     }
    
    /**
    *返回一个文件相对于另一个文件的路径。
    *
    *@param target指向目标目录
    *@param base基本目录
    *@return目标相对于基目录的路径
    *@如果在解析文件的规范名时发生错误,则会引发IOException
    */
    公共静态文件getRelativeFile(文件目标、文件基)引发IOException
    {
    字符串[]baseComponents=base.getCanonicalPath().split(Pattern.quote(File.separator));
    字符串[]targetComponents=target.getCanonicalPath().split(Pattern.quote(File.separator));
    //跳过通用组件
    int指数=0;
    对于(;index
    Matt B的解决方案得到了要回溯的目录数错误——它应该是基本路径的长度减去公共路径元素的数量,再减去一(对于最后一个路径元素,它是文件名或由
    拆分生成的后续
    )。它恰好与
    /a/b/c/
    /a/x/y/
    一起工作,但将参数替换为
    /m/n/o/a/b/c/
    /m/n/o/a/x/y/
    ,您将看到问题

    此外,它还需要在第一个for循环中使用
    else break
    ,否则它将错误处理恰好具有匹配目录名的路径,例如
    /a/b/c/d/
    /x/y/c/z
    ——这两个数组中的
    c
    位于同一插槽中,但不是实际匹配的路径

    所有这些解决方案都缺乏处理无法相互关联的路径的能力,因为它们的根不兼容,例如
    C:\foo\bar
    D:\baz\qux
    。这可能只是Windows上的一个问题,但值得注意

    我花在这上面的时间比我预期的要长得多,但没关系。我实际上是为了工作才需要这个,所以感谢所有参与进来的人,我相信这个版本也会有修改

    public static String getRelativePath(String targetPath, String basePath, 
            String pathSeparator) {
    
        //  We need the -1 argument to split to make sure we get a trailing 
        //  "" token if the base ends in the path separator and is therefore
        //  a directory. We require directory paths to end in the path
        //  separator -- otherwise they are indistinguishable from files.
        String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
        String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);
    
        //  First get all the common elements. Store them as a string,
        //  and also count how many of them there are. 
        String common = "";
        int commonIndex = 0;
        for (int i = 0; i < target.length && i < base.length; i++) {
            if (target[i].equals(base[i])) {
                common += target[i] + pathSeparator;
                commonIndex++;
            }
            else break;
        }
    
        if (commonIndex == 0)
        {
            //  Whoops -- not even a single common path element. This most
            //  likely indicates differing drive letters, like C: and D:. 
            //  These paths cannot be relativized. Return the target path.
            return targetPath;
            //  This should never happen when all absolute paths
            //  begin with / as in *nix. 
        }
    
        String relative = "";
        if (base.length == commonIndex) {
            //  Comment this out if you prefer that a relative path not start with ./
            //relative = "." + pathSeparator;
        }
        else {
            int numDirsUp = base.length - commonIndex - 1;
            //  The number of directories we have to backtrack is the length of 
            //  the base path MINUS the number of common path elements, minus
            //  one because the last element in the path isn't a directory.
            for (int i = 1; i <= (numDirsUp); i++) {
                relative += ".." + pathSeparator;
            }
        }
        relative += targetPath.substring(common.length());
    
        return relative;
    }
    
    中提到的错误由中解决

    根据 基本URI。解决中的bug java.net.URI()


    酷!!我需要一点这样的代码,但用于比较Linux机器上的目录路径。我发现这在以父目录为目标的情况下不起作用

    以下是该方法的目录友好版本:

     public static String getRelativePath(String targetPath, String basePath, 
         String pathSeparator) {
    
     boolean isDir = false;
     {
       File f = new File(targetPath);
       isDir = f.isDirectory();
     }
     //  We need the -1 argument to split to make sure we get a trailing 
     //  "" token if the base ends in the path separator and is therefore
     //  a directory. We require directory paths to end in the path
     //  separator -- otherwise they are indistinguishable from files.
     String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
     String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);
    
     //  First get all the common elements. Store them as a string,
     //  and also count how many of them there are. 
     String common = "";
     int commonIndex = 0;
     for (int i = 0; i < target.length && i < base.length; i++) {
         if (target[i].equals(base[i])) {
             common += target[i] + pathSeparator;
             commonIndex++;
         }
         else break;
     }
    
     if (commonIndex == 0)
     {
         //  Whoops -- not even a single common path element. This most
         //  likely indicates differing drive letters, like C: and D:. 
         //  These paths cannot be relativized. Return the target path.
         return targetPath;
         //  This should never happen when all absolute paths
         //  begin with / as in *nix. 
     }
    
     String relative = "";
     if (base.length == commonIndex) {
         //  Comment this out if you prefer that a relative path not start with ./
         relative = "." + pathSeparator;
     }
     else {
         int numDirsUp = base.length - commonIndex - (isDir?0:1); /* only subtract 1 if it  is a file. */
         //  The number of directories we have to backtrack is the length of 
         //  the base path MINUS the number of common path elements, minus
         //  one because the last element in the path isn't a directory.
         for (int i = 1; i <= (numDirsUp); i++) {
             relative += ".." + pathSeparator;
         }
     }
     //if we are comparing directories then we 
     if (targetPath.length() > common.length()) {
      //it's OK, it isn't a directory
      relative += targetPath.substring(common.length());
     }
    
     return relative;
    }
    
    公共静态字符串getRelativePath(字符串targetPath、字符串basePath、,
    字符串路径分隔符){
    布尔isDir=false;
    {
    文件f=新文件(targetPath);
    isDirectory=f.isDirectory();
    }
    //我们需要拆分-1参数以确保
    
    public static URI resolve(URI baseURI,
                              String reference)
    
     public static String getRelativePath(String targetPath, String basePath, 
         String pathSeparator) {
    
     boolean isDir = false;
     {
       File f = new File(targetPath);
       isDir = f.isDirectory();
     }
     //  We need the -1 argument to split to make sure we get a trailing 
     //  "" token if the base ends in the path separator and is therefore
     //  a directory. We require directory paths to end in the path
     //  separator -- otherwise they are indistinguishable from files.
     String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
     String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);
    
     //  First get all the common elements. Store them as a string,
     //  and also count how many of them there are. 
     String common = "";
     int commonIndex = 0;
     for (int i = 0; i < target.length && i < base.length; i++) {
         if (target[i].equals(base[i])) {
             common += target[i] + pathSeparator;
             commonIndex++;
         }
         else break;
     }
    
     if (commonIndex == 0)
     {
         //  Whoops -- not even a single common path element. This most
         //  likely indicates differing drive letters, like C: and D:. 
         //  These paths cannot be relativized. Return the target path.
         return targetPath;
         //  This should never happen when all absolute paths
         //  begin with / as in *nix. 
     }
    
     String relative = "";
     if (base.length == commonIndex) {
         //  Comment this out if you prefer that a relative path not start with ./
         relative = "." + pathSeparator;
     }
     else {
         int numDirsUp = base.length - commonIndex - (isDir?0:1); /* only subtract 1 if it  is a file. */
         //  The number of directories we have to backtrack is the length of 
         //  the base path MINUS the number of common path elements, minus
         //  one because the last element in the path isn't a directory.
         for (int i = 1; i <= (numDirsUp); i++) {
             relative += ".." + pathSeparator;
         }
     }
     //if we are comparing directories then we 
     if (targetPath.length() > common.length()) {
      //it's OK, it isn't a directory
      relative += targetPath.substring(common.length());
     }
    
     return relative;
    }
    
    import java.io.File;
    import java.util.regex.Pattern;
    
    import org.apache.commons.io.FilenameUtils;
    
    public class ResourceUtils {
    
        /**
         * Get the relative path from one file to another, specifying the directory separator. 
         * If one of the provided resources does not exist, it is assumed to be a file unless it ends with '/' or
         * '\'.
         * 
         * @param targetPath targetPath is calculated to this file
         * @param basePath basePath is calculated from this file
         * @param pathSeparator directory separator. The platform default is not assumed so that we can test Unix behaviour when running on Windows (for example)
         * @return
         */
        public static String getRelativePath(String targetPath, String basePath, String pathSeparator) {
    
            // Normalize the paths
            String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
            String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);
    
            // Undo the changes to the separators made by normalization
            if (pathSeparator.equals("/")) {
                normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
                normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);
    
            } else if (pathSeparator.equals("\\")) {
                normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
                normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);
    
            } else {
                throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'");
            }
    
            String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
            String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));
    
            // First get all the common elements. Store them as a string,
            // and also count how many of them there are.
            StringBuffer common = new StringBuffer();
    
            int commonIndex = 0;
            while (commonIndex < target.length && commonIndex < base.length
                    && target[commonIndex].equals(base[commonIndex])) {
                common.append(target[commonIndex] + pathSeparator);
                commonIndex++;
            }
    
            if (commonIndex == 0) {
                // No single common path element. This most
                // likely indicates differing drive letters, like C: and D:.
                // These paths cannot be relativized.
                throw new PathResolutionException("No common path element found for '" + normalizedTargetPath + "' and '" + normalizedBasePath
                        + "'");
            }   
    
            // The number of directories we have to backtrack depends on whether the base is a file or a dir
            // For example, the relative path from
            //
            // /foo/bar/baz/gg/ff to /foo/bar/baz
            // 
            // ".." if ff is a file
            // "../.." if ff is a directory
            //
            // The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because
            // the resource referred to by this path may not actually exist, but it's the best I can do
            boolean baseIsFile = true;
    
            File baseResource = new File(normalizedBasePath);
    
            if (baseResource.exists()) {
                baseIsFile = baseResource.isFile();
    
            } else if (basePath.endsWith(pathSeparator)) {
                baseIsFile = false;
            }
    
            StringBuffer relative = new StringBuffer();
    
            if (base.length != commonIndex) {
                int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex;
    
                for (int i = 0; i < numDirsUp; i++) {
                    relative.append(".." + pathSeparator);
                }
            }
            relative.append(normalizedTargetPath.substring(common.length()));
            return relative.toString();
        }
    
    
        static class PathResolutionException extends RuntimeException {
            PathResolutionException(String msg) {
                super(msg);
            }
        }    
    }
    
    public void testGetRelativePathsUnix() {
        assertEquals("stuff/xyz.dat", ResourceUtils.getRelativePath("/var/data/stuff/xyz.dat", "/var/data/", "/"));
        assertEquals("../../b/c", ResourceUtils.getRelativePath("/a/b/c", "/a/x/y/", "/"));
        assertEquals("../../b/c", ResourceUtils.getRelativePath("/m/n/o/a/b/c", "/m/n/o/a/x/y/", "/"));
    }
    
    public void testGetRelativePathFileToFile() {
        String target = "C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";
        String base = "C:\\Windows\\Speech\\Common\\sapisvr.exe";
    
        String relPath = ResourceUtils.getRelativePath(target, base, "\\");
        assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);
    }
    
    public void testGetRelativePathDirectoryToFile() {
        String target = "C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";
        String base = "C:\\Windows\\Speech\\Common\\";
    
        String relPath = ResourceUtils.getRelativePath(target, base, "\\");
        assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);
    }
    
    public void testGetRelativePathFileToDirectory() {
        String target = "C:\\Windows\\Boot\\Fonts";
        String base = "C:\\Windows\\Speech\\Common\\foo.txt";
    
        String relPath = ResourceUtils.getRelativePath(target, base, "\\");
        assertEquals("..\\..\\Boot\\Fonts", relPath);
    }
    
    public void testGetRelativePathDirectoryToDirectory() {
        String target = "C:\\Windows\\Boot\\";
        String base = "C:\\Windows\\Speech\\Common\\";
        String expected = "..\\..\\Boot";
    
        String relPath = ResourceUtils.getRelativePath(target, base, "\\");
        assertEquals(expected, relPath);
    }
    
    public void testGetRelativePathDifferentDriveLetters() {
        String target = "D:\\sources\\recovery\\RecEnv.exe";
        String base = "C:\\Java\\workspace\\AcceptanceTests\\Standard test data\\geo\\";
    
        try {
            ResourceUtils.getRelativePath(target, base, "\\");
            fail();
    
        } catch (PathResolutionException ex) {
            // expected exception
        }
    }
    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Test {
    
         public static void main(String[] args) {
            Path pathAbsolute = Paths.get("/var/data/stuff/xyz.dat");
            Path pathBase = Paths.get("/var/data");
            Path pathRelative = pathBase.relativize(pathAbsolute);
            System.out.println(pathRelative);
        }
    
    }
    
    stuff/xyz.dat
    
    /**
     * Computes the path for a file relative to a given base, or fails if the only shared 
     * directory is the root and the absolute form is better.
     * 
     * @param base File that is the base for the result
     * @param name File to be "relativized"
     * @return the relative name
     * @throws IOException if files have no common sub-directories, i.e. at best share the
     *                     root prefix "/" or "C:\"
     */
    
    public static String getRelativePath(File base, File name) throws IOException  {
        File parent = base.getParentFile();
    
        if (parent == null) {
            throw new IOException("No common directory");
        }
    
        String bpath = base.getCanonicalPath();
        String fpath = name.getCanonicalPath();
    
        if (fpath.startsWith(bpath)) {
            return fpath.substring(bpath.length() + 1);
        } else {
            return (".." + File.separator + getRelativePath(parent, name));
        }
    }
    
    public static String getRelativePath (String baseDir, String targetPath) {
        String[] base = baseDir.replace('\\', '/').split("\\/");
        targetPath = targetPath.replace('\\', '/');
        String[] target = targetPath.split("\\/");
    
        // Count common elements and their length.
        int commonCount = 0, commonLength = 0, maxCount = Math.min(target.length, base.length);
        while (commonCount < maxCount) {
            String targetElement = target[commonCount];
            if (!targetElement.equals(base[commonCount])) break;
            commonCount++;
            commonLength += targetElement.length() + 1; // Directory name length plus slash.
        }
        if (commonCount == 0) return targetPath; // No common path element.
    
        int targetLength = targetPath.length();
        int dirsUp = base.length - commonCount;
        StringBuffer relative = new StringBuffer(dirsUp * 3 + targetLength - commonLength + 1);
        for (int i = 0; i < dirsUp; i++)
            relative.append("../");
        if (commonLength < targetLength) relative.append(targetPath.substring(commonLength));
        return relative.toString();
    }
    
    public static String GetRelativePath(String path, String base){
    
        final String SEP = "/";
    
        // if base is not a directory -> return empty
        if (!base.endsWith(SEP)){
            return "";
        }
    
        // check if path is a file -> remove last "/" at the end of the method
        boolean isfile = !path.endsWith(SEP);
    
        // get URIs and split them by using the separator
        String a = "";
        String b = "";
        try {
            a = new File(base).getCanonicalFile().toURI().getPath();
            b = new File(path).getCanonicalFile().toURI().getPath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String[] basePaths = a.split(SEP);
        String[] otherPaths = b.split(SEP);
    
        // check common part
        int n = 0;
        for(; n < basePaths.length && n < otherPaths.length; n ++)
        {
            if( basePaths[n].equals(otherPaths[n]) == false )
                break;
        }
    
        // compose the new path
        StringBuffer tmp = new StringBuffer("");
        for(int m = n; m < basePaths.length; m ++)
            tmp.append(".."+SEP);
        for(int m = n; m < otherPaths.length; m ++)
        {
            tmp.append(otherPaths[m]);
            tmp.append(SEP);
        }
    
        // get path string
        String result = tmp.toString();
    
        // remove last "/" if path is a file
        if (isfile && result.endsWith(SEP)){
            result = result.substring(0,result.length()-1);
        }
    
        return result;
    }
    
    Path sourceFile = Paths.get("some/common/path/example/a/b/c/f1.txt");
    Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt"); 
    Path relativePath = sourceFile.relativize(targetFile);
    System.out.println(relativePath);
    
    ..\..\..\..\d\e\f2.txt
    
    Path sourceFile = Paths.get(new File("some/common/path/example/a/b/c/f1.txt").parent());
    Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt"); 
    Path relativePath = sourceFile.relativize(targetFile);
    System.out.println(relativePath);
    
    private String relative(String left, String right){
        String[] lefts = left.split("/");
        String[] rights = right.split("/");
        int min = Math.min(lefts.length, rights.length);
        int commonIdx = -1;
        for(int i = 0; i < min; i++){
            if(commonIdx < 0 && !lefts[i].equals(rights[i])){
                commonIdx = i - 1;
                break;
            }
        }
        if(commonIdx < 0){
            return null;
        }
        StringBuilder sb = new StringBuilder(Math.max(left.length(), right.length()));
        sb.append(left).append("/");
        for(int i = commonIdx + 1; i < lefts.length;i++){
            sb.append("../");
        }
        for(int i = commonIdx + 1; i < rights.length;i++){
            sb.append(rights[i]).append("/");
        }
    
        return sb.deleteCharAt(sb.length() -1).toString();
    }
    
    Path#relativize(Path)
    
    import static java.util.Arrays.asList;
    import static java.util.Collections.nCopies;
    import static org.apache.commons.io.FilenameUtils.normalizeNoEndSeparator;
    import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
    import static org.apache.commons.lang3.StringUtils.getCommonPrefix;
    import static org.apache.commons.lang3.StringUtils.isBlank;
    import static org.apache.commons.lang3.StringUtils.isNotEmpty;
    import static org.apache.commons.lang3.StringUtils.join;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    public class ResourceUtils {
    
        public static String getRelativePath(String targetPath, String basePath, String pathSeparator) {
            File baseFile = new File(basePath);
            if (baseFile.isFile() || !baseFile.exists() && !basePath.endsWith("/") && !basePath.endsWith("\\"))
                basePath = baseFile.getParent();
    
            String target = separatorsToUnix(normalizeNoEndSeparator(targetPath));
            String base = separatorsToUnix(normalizeNoEndSeparator(basePath));
    
            String commonPrefix = getCommonPrefix(target, base);
            if (isBlank(commonPrefix))
                return targetPath.replaceAll("/", pathSeparator);
    
            target = target.replaceFirst(commonPrefix, "");
            base = base.replaceFirst(commonPrefix, "");
    
            List<String> result = new ArrayList<>();
            if (isNotEmpty(base))
                result.addAll(nCopies(base.split("/").length, ".."));
            result.addAll(asList(target.replaceFirst("^/", "").split("/")));
    
            return join(result, pathSeparator);
        }
    }
    
    package org.afc.util;
    
    import java.io.File;
    import java.util.LinkedList;
    import java.util.List;
    
    public class FileUtil {
    
        public static String getRelativePath(String basePath, String filePath)  {
            return getRelativePath(new File(basePath), new File(filePath));
        }
    
        public static String getRelativePath(File base, File file)  {
    
            List<String> bases = new LinkedList<String>();
            bases.add(0, base.getName());
            for (File parent = base.getParentFile(); parent != null; parent = parent.getParentFile()) {
                bases.add(0, parent.getName());
            }
    
            List<String> files = new LinkedList<String>();
            files.add(0, file.getName());
            for (File parent = file.getParentFile(); parent != null; parent = parent.getParentFile()) {
                files.add(0, parent.getName());
            }
    
            int overlapIndex = 0;
            while (overlapIndex < bases.size() && overlapIndex < files.size() && bases.get(overlapIndex).equals(files.get(overlapIndex))) {
                overlapIndex++;
            }
    
            StringBuilder relativePath = new StringBuilder();
            for (int i = overlapIndex; i < bases.size(); i++) {
                relativePath.append("..").append(File.separatorChar);
            }
    
            for (int i = overlapIndex; i < files.size(); i++) {
                relativePath.append(files.get(i)).append(File.separatorChar);
            }
    
            relativePath.deleteCharAt(relativePath.length() - 1);
            return relativePath.toString();
        }
    
    }
    
        public static String getRealtivePath(File root, File file) 
        {
            String path = file.getPath();
            String rootPath = root.getPath();
            boolean plus1 = path.contains(File.separator);
            return path.substring(path.indexOf(rootPath) + rootPath.length() + (plus1 ? 1 : 0));
        }