簡體   English   中英

如何獲取 Java JAR 文件中資源的路徑

[英]How to get a path to a resource in a Java JAR file

我正在嘗試獲取資源的路徑,但我沒有運氣。

這有效(在 IDE 和 JAR 中),但這樣我無法獲得文件的路徑,只能獲得文件內容:

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

如果我這樣做:

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

結果是: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)那個java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

有沒有辦法獲取資源文件的路徑?

這是故意的。 “文件”的內容可能無法作為文件使用。 請記住,您正在處理可能是 JAR 文件或其他類型資源的一部分的類和資源。 類加載器不必為資源提供文件句柄,例如 jar 文件可能沒有被擴展為文件系統中的單個文件。

如果絕對需要 java.io.File,則可以通過將流復制到臨時文件中並執行相同操作來通過獲取 java.io.File 來完成任何操作。

加載資源時,請確保您注意到以下區別:

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

我想,在加載資源時,這種混亂會導致大多數問題。


此外,當您加載圖像時,使用getResourceAsStream()更容易:

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

當您真的必須從 JAR 存檔中加載(非圖像)文件時,您可以嘗試以下操作:

File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile("tempfile", ".tmp");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}

if (file != null && !file.exists()) {
    throw new RuntimeException("Error: File " + file + " not found!");
}

單行答案是——

String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()

基本上getResource方法給出了 URL。 從此 URL 中,您可以通過調用toExternalForm()來提取路徑。

我花了一段時間來解決這個問題,因為我發現沒有一個真正有效的解決方案,奇怪的是! 工作目錄通常不是 JAR 的目錄,尤其是在 Windows 下從“開始”菜單運行 JAR(或任何程序)時。 所以這就是我所做的,它適用於從 JAR 外部運行的 .class 文件,就像它適用於 JAR 一樣。 (我只在 Windows 7 下測試過。)

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}

這是來自用戶 Tombart 的相同代碼,帶有流刷新和關閉,以避免從 jar 資源復制不完整的臨時文件內容並具有唯一的臨時文件名。

File file = null;
String resource = "/view/Trial_main.html" ;
URL res = getClass().getResource(resource);
if (res.toString().startsWith("jar:")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile(new Date().getTime()+"", ".html");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.flush();
        out.close();
        input.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}
         

如果netclient.p在 JAR 文件中,它不會有路徑,因為該文件位於其他文件中。 在這種情況下,您可以擁有的最佳路徑實際上是file:/path/to/jarfile/bot.jar!/config/netclient.p

您需要了解 jar 文件中的路徑。
簡單地參考它相對。 因此,如果您有一個文件 (myfile.txt),位於\\src\\main\\resources目錄下的 foo.jar(maven 樣式)。 你會像這樣引用它:

src/main/resources/myfile.txt

如果您使用jar -tvf myjar.jar轉儲您的 jar,您將看到 jar 文件中的輸出和相對路徑,並使用 FORWARD SLASHES。

就我而言,我使用了 URL 對象而不是 Path。

文件

File file = new File("my_path");
URL url = file.toURI().toURL();

使用類加載器的類路徑中的資源

URL url = MyClass.class.getClassLoader().getResource("resource_name")

當我需要閱讀內容時,可以使用以下代碼:

InputStream stream = url.openStream();

您可以使用 InputStream 訪問內容。

文件是文件系統中文件的抽象,文件系統對 JAR 的內容一無所知。

嘗試使用 URI,我認為有一個 jar:// 協議可能對您的目的有用。

以下路徑對我classpath:/path/to/resource/in/jarclasspath:/path/to/resource/in/jar

private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

getSystemResourceAsStream獲取它是最好的選擇。 通過獲取輸入流而不是文件或 URL,可以在 JAR 文件中獨立工作。

在你的 jar 的 ressources 文件夾(java/main/resources)中添加你的文件(我們假設你已經添加了一個名為import.xml的 xml 文件),然后你注入ResourceLoader如果你像下面這樣使用 spring

@Autowired
private ResourceLoader resourceLoader;

內部游覽功能編寫波紋管代碼以加載文件:

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}

可能有點晚了,但您可以使用我的庫KResourceLoader從您的 jar 中獲取資源:

File resource = getResource("file.txt")

jar文件中時,資源絕對位於包層次結構(而不​​是文件系統層次結構)中。 因此,如果您讓com.example.Sweet類加載名為./default.conf的資源,則該資源的名稱指定為/com/example/default.conf

但是如果它在一個罐子里,那么它就不是一個文件......

也許這種方法可以用於快速解決。

public class TestUtility
{ 
    public static File getInternalResource(String relativePath)
    {
        File resourceFile = null;
        URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
        String codeLocation = location.toString();
        try{
            if (codeLocation.endsWith(".jar"){
                //Call from jar
                Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
                resourceFile = path.toFile();
            }else{
                //Call from IDE
                resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
            }
        }catch(URISyntaxException ex){
            ex.printStackTrace();
        }
        return resourceFile;
    }
}

跟隨代碼!

/src/main/resources/file

streamToFile(getClass().getClassLoader().getResourceAsStream("file"))

public static File streamToFile(InputStream in) {
    if (in == null) {
        return null;
    }

    try {
        File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        f.deleteOnExit();

        FileOutputStream out = new FileOutputStream(f);
        byte[] buffer = new byte[1024];

        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        return f;
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        return null;
    }
}

這個Class函數可以通過.jar文件中的相對文件路徑獲取絕對文件路徑。


public class Utility {

    public static void main(String[] args) throws Exception {
        Utility utility = new Utility();
        String absolutePath = utility.getAbsolutePath("./absolute/path/to/file");
    }

    public String getAbsolutePath(String relativeFilePath) throws IOException {
        URL url = this.getClass().getResource(relativeFilePath);
        return url.getPath();
    }
}

如果目標文件與Utility.java位於同一目錄,則您的輸入將為./file.txt

當您僅在 getAbsolutePath() 上輸入/時,它會返回/Users/user/PROJECT_NAME/target/classes/ 這意味着您可以像這樣選擇文件/com/example/file.txt

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM