【问题标题】:Get current path of executed file获取执行文件的当前路径
【发布时间】:2018-12-29 23:40:28
【问题描述】:

我尝试在名为 Books.txt 的 java 项目文件中写入和读取文件。 问题是我只能在 partialPath 具有文件的完整路径时才能访问该文件。

代码如下:

public <T> List<T> readFromFile(String fileName) {

    private String partialPath = "\\HW3\\src\\java\\repos\\";

    try {
        String path = partialPath + fileName;
        FileInputStream fi = new FileInputStream(path);
        ObjectInputStream oi = new ObjectInputStream(fi);
        // Read objects
        List<T> items = (List<T>) oi.readObject();
        oi.close();
        fi.close();

        return items;
    } catch (IOException | ClassNotFoundException e) {

    }
  }

如果我像上面那样设置相对路径,我会找不到异常文件。

我的问题是如何以编程方式设置当前目录的完整路径?

【问题讨论】:

  • 如果你想写入文件,那么它不能存在于src - 你不应该直接引用src,因为一旦项目被导出,它就不会存在。相反,您可以尝试将文件放在当前工作目录中。您可以使用System.getProperty("user.dir") 确定工作目录。问题在于,它可能会根据程序的执行位置而改变。 “更通用”的方法是使用“众所周知”的位置,例如用户的主目录。您可以使用System.getProperty("user.home") 来查找,但每个操作系统都有自己的规则
  • 当前目录只是“.”。问题是您的项目的工作目录在哪里,这在一定程度上取决于您使用的 IDE。这可能不是 src 而是一个文件夹。如果您从命令行运行程序,它将与您启动程序的目录相同。
  • @Michael "我可以把文件放在项目目录中吗?" - 你可以把文件放在任何喜欢的地方,问题是试图再次找到它们。要使这种事情发挥作用,您需要解决各种限制和问题。这也是一个常见的问题,不只是在 Java 中,它有一个相对简单的解决方案 - 使用“众所周知的位置”来存储文件,假设您想要对它们进行读/写访问
  • 这是错误的方法。多年来,操作系统制造商(和 Oracle)一直在说不要将应用程序数据存储在应用程序的目录中。检查this answer 以获得更好的方法。哦,我看到@MadProgrammer 已经击败了我:“使用“众所周知的位置”来存储文件”

标签: java


【解决方案1】:

这是我编写的Drombler Commons - Client Startup 代码中的code snippet,用于确定可执行jar 的位置。将DromblerClientStarter 替换为您的主类。 至少当您将应用程序作为可执行 JAR 文件运行时,这应该可以工作。

/**
 * The jar URI prefix "jar:"
 */
private static final String FULL_JAR_URI_PREFIX = "jar:";
/**
 * Length of the jar URI prefix "jar:"
 */
private static final int FULL_JAR_URI_PREFIX_LENGTH = 4;

private Path determineMainJarPath() throws URISyntaxException {
    Class<DromblerClientStarter> type = DromblerClientStarter.class;
    String jarResourceURIString = type.getResource("/" + type.getName().replace(".", "/") + ".class").toURI().
            toString();
    int endOfJarPathIndex = jarResourceURIString.indexOf("!/");
    String mainJarURIString = endOfJarPathIndex >= 0 ? jarResourceURIString.substring(0, endOfJarPathIndex)
            : jarResourceURIString;
    if (mainJarURIString.startsWith(FULL_JAR_URI_PREFIX)) {
        mainJarURIString = mainJarURIString.substring(FULL_JAR_URI_PREFIX_LENGTH);
    }
    Path mainJarPath = Paths.get(URI.create(mainJarURIString));
    return mainJarPath;
}

根据您在应用程序分发包中捆绑 Books.txt 的位置,您可以使用此 mainJarPath 来确定 Books.txt 的路径。

【讨论】:

  • Tnx for post.什么是主类?
  • 我是java新手,但我有很多.net背景。
  • 主类是你调用的主方法所在的类。
  • 对于 Java 初学者,我还推荐 Oracle Java 教程,尽管它们似乎只涵盖 Java 8 而不是 Java 11:docs.oracle.com/javase/tutorial
  • 恕我直言,这似乎就像用大锤敲碎花生。一个更简单的解决方案很简单,尝试解决文件驻留的“众所周知”位置,该位置独立于 Jar 文件所在的位置或执行位置,这是一个已经在使用的足够常见的问题和解决方案模式 -只是说
【解决方案2】:

我还认为,由您正在运行的 Java 应用程序创建(随后可能会修改或删除)的文件通常最好放置在远离您的 Java 应用程序安装主目录的文件系统位置。一个示例可能是适用于 Windows 操作系统的 'C:\ProgramData\ApplicationNameFiles\' 或类似的 other OS platforms。在我看来,至少对我而言,我认为由于驱动器维护不善或用户意外删除打开文件资源管理器并决定自行承担它而导致基本应用程序文件损坏的可能性较小清理他们的系统中所谓的不必要文件,以及其他不那么明显的原因。

因为 Java 几乎可以在任何平台上运行,并且此类数据文件位置是特定于平台的,所以应允许用户选择可以创建和操作这些文件的位置。然后可以将此位置保存为属性。确实,工作量稍微多一些,但恕我直言,我觉得这可能是值得的。

当 JAR 文件第一次启动时,在它的安装主目录中创建一个目录(文件夹)显然要容易得多,然后从那里存储和操作应用程序创建的数据文件。绝对更容易找到,但话又说回来......这将是一个见仁见智的问题,它不会是我的。无论如何,如果您一心想要这样做,那么您的 Java 应用程序的安装实用程序肯定应该知道该安装路径的位置,因此只需将该位置存储在某处即可。

没有安装实用程序?那么你的 Java 应用程序肯定需要一种方法来知道你的 JAR 文件从哪里运行,下面的代码是一种方法:

public String applicationPath(Class mainStartupClassName) {
    try {
        String path = mainStartupClassName.getProtectionDomain().getCodeSource().getLocation().getPath();
        String pathDecoded = URLDecoder.decode(path, "UTF-8");
        pathDecoded = pathDecoded.trim().replace("/", File.separator);
        if (pathDecoded.startsWith(File.separator)) {
            pathDecoded = pathDecoded.substring(1);
        }
        return pathDecoded;
    }
    catch (UnsupportedEncodingException ex) {
        Logger.getLogger("applicationPath() Method").log(Level.SEVERE, null, ex);
    }
    return null;
}

以下是您将如何使用此方法:

String appPath = applicationPath(MyMainStartupClassName.class);

请记住,如果在 IDE 中运行此方法,它很可能不会返回 JAR 文件的路径,而是指向为应用程序存储类的文件夹构建。

【讨论】:

    【解决方案3】:

    这不是 Java 独有的问题,任何希望将数据本地写入磁盘的任何语言的开发人员都会面临这个问题。这个问题有很多部分。

    如果您希望能够写入文件(并且可能读取更改),那么您需要设计一种解决方案,允许您以独立于平台的方式查找文件。

    一些问题

    程序的安装位置

    虽然大多数操作系统确实有一些规范这一点,但这并不意味着它们总是被使用,无论出于何种原因。

    此外,在某些操作系统上,您被主动限制写入“安装”位置。 Windows 8+ 不允许您写入“程序文件”目录,而在 Java 中,这通常(或至少在我处理它时)会静默失败。

    在 MacOS 上,如果您使用的是“应用程序包”,工作目录会自动设置为用户的主目录,从而更加难以管理

    执行上下文(或工作目录)可能与程序的安装位置不同

    一个程序可以安装在一个位置,但从不同的位置执行,这将改变工作目录的位置。许多命令行工具都遇到了这个问题,并使用不同的约定来解决它(想知道JAVA_HOME 环境变量是什么?)

    限制磁盘访问

    许多操作系统现在都在积极锁定程序可以写入的位置,即使具有管理员权限也是如此。

    可重复使用的解决方案...

    大多数操作系统都提出了解决此问题的约定,不仅适用于 Java,而且适用于所有希望在该平台上工作的开发人员。

    重要与所有指南一样,这些不是硬性规定,而是平台作者提出的建议,旨在让您的生活更简单,让平台的运营更安全

    最常见的解决方案是简单地将文件放在磁盘上的“众所周知的位置”,可以通过绝对路径访问该文件,而与程序的安装或执行位置无关。

    在 Windows 上,这意味着将文件放在 ~\AppData\Local\{application name}~\AppData\Roaming\{application name}

    在 MacOS 上,这意味着将文件放在 ~/Library/Application Data/{application name}

    在 *nix 上,这通常意味着将文件放在 ~/.{application name}

    可以说你可以在所有三个平台上使用~/.{application name},但作为一个“显示隐藏文件”的用户,我希望你不要污染我的主目录。

    一种可能的、可重复使用的解决方案...

    当 Windows 8 推出时,我遇到了“您无法写入 Program Files”的问题,这需要一些时间来诊断,因为它没有生成异常,只是失败了。

    我还在 Mac OS 上工作了很多,所以我需要一个简单的跨平台解决方案,这样我的代码就可以自动适应,而不需要每个平台有多个分支。

    为此,我带来了一个简单的实用程序类...

    public enum SystemUtilities {
    
        INSTANCE;
    
        public boolean isMacOS() {
            return getOSName().startsWith("Mac");
        }
    
        public boolean isMacOSX() {
            return getOSName().startsWith("Mac OS X");
        }
    
        public boolean isWindowsOS() {
            return getOSName().startsWith("Windows");
        }
    
        public boolean isLinux() {
            return getOSName().startsWith("Linux");
        }
    
        public String getOSName() {
            return System.getProperty("os.name");
        }
    
        public File getRoamingApplicationSupportPath() {
            // For *inx, use '~/.{AppName}'
            String path = System.getProperty("user.home");
            if (isWindowsOS()) {
                path += "\\AppData\\Roaming";
            } else if (isMacOS()) {
                path += "/Library/Application Support";
            }
            return new File(path);
        }
    
        public File getLocalApplicationSupportPath() {
            // For *inx, use '~/.{AppName}'
            String path = System.getProperty("user.home");
            if (isWindowsOS()) {
                path += "\\AppData\\Local";
            } else if (isMacOS()) {
                path += "/Library/Application Support";
            }
            return new File(path);
        }
    
    }
    

    这提供了一个可以构建“独立”代码的基线,例如,您可以使用类似...

    File appDataDir = new File(SystemUtilities.INSTANCE.getLocalApplicationSupportPath(), "MyAwesomeApp");
    if (appDataDir.exists() || appDataDir.mkdirs()) {
        File fileToWrite = new File(appDataDir, "Books.txt");
        //...
    }
    

    读取/写入文件。虽然,就我个人而言,我可能会让经理/工厂做这项工作并将参考返回到结尾File,但那是我。

    “预打包”文件呢?

    三种可能的解决方案...

    1. 如果文件不存在,则创建文件,并根据需要使用默认值填充它们
    2. 从 Jar 文件中复制“模板”文件(如果它们不存在)
    3. 使用安装程序安装文件 - 这是我们在面临更改所有“外部”配置文件的位置时使用的解决方案。

    只读文件...

    对于只读文件,最简单的解决方案是将它们作为“嵌入资源”嵌入到 Jar 中,这样更容易定位和管理...

    URL url = getClass().getResource("/path/to/readOnlyResource.txt");
    

    如何做到这一点,取决于你的构建系统

    【讨论】:

      猜你喜欢
      • 2018-10-17
      • 2017-04-16
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 2021-11-02
      相关资源
      最近更新 更多