Cách nhận đường dẫn đến tài nguyên trong tệp JAR Java


166

Tôi đang cố gắng để có được một con đường đến một Tài nguyên nhưng tôi đã không gặp may mắn.

Điều này hoạt động (cả trong IDE và với JAR) nhưng theo cách này tôi không thể có đường dẫn đến tệp, chỉ có nội dung tệp:

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

Nếu tôi làm điều này:

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

Kết quả là: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

Có cách nào để có được đường dẫn đến tệp tài nguyên không?


1
Đúng. Tôi đã có một lớp mà tôi muốn làm việc với cả hai, một thư mục ở bên ngoài (trong trường hợp tôi muốn thay đổi một số tham số của tệp cấu hình) và một tệp JAR ẩn các tệp cấu hình thực hiện cho người dùng (như phân phối JAR cho tất cả mọi người).
no_ripcord

1
Vì vậy, lớp chỉ nhận được một PATH cho một tệp (tệp cấu hình).
no_ripcord

3
Sau đó, bạn có thể nên có lớp đó xử lý một luồng đầu vào mà bạn có thể nhận được từ một trong hai nguồn.
Carl Manaster

1
Vâng tôi biết. Nhưng nó sẽ rõ ràng hơn và làm sạch theo cách khác. Nhưng dù sao đi nữa.
no_ripcord

1
Bạn đang cố gắng làm một cái gì đó như tùy chọn # 4 trong câu hỏi này? stackoverflow.com/questions/775389/ cường
erickson

Câu trả lời:


70

Đây là cố ý. Nội dung của "tệp" có thể không có sẵn dưới dạng tệp. Hãy nhớ rằng bạn đang xử lý các lớp và tài nguyên có thể là một phần của tệp JAR hoặc loại tài nguyên khác. Trình nạp lớp không phải cung cấp xử lý tệp cho tài nguyên, ví dụ tệp jar có thể không được mở rộng thành các tệp riêng lẻ trong hệ thống tệp.

Bất cứ điều gì bạn có thể làm bằng cách lấy java.io.File có thể được thực hiện bằng cách sao chép luồng ra thành tệp tạm thời và thực hiện tương tự, nếu java.io.File là hoàn toàn cần thiết.


6
bạn có thể thêm 'rsrc:' khi bạn gọi tài nguyên của mình để mở nó. như Tệp mới ("rsrc: filename.txt"), tệp này sẽ tải filename.txt được đóng gói bên trong thư mục gốc của bạn
gipsh

63

Khi tải tài nguyên, đảm bảo bạn nhận thấy sự khác biệt giữa:

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

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

Tôi đoán, sự nhầm lẫn này đang gây ra hầu hết các vấn đề khi tải tài nguyên.


Ngoài ra, khi bạn đang tải một hình ảnh, nó dễ sử dụng hơn getResourceAsStream():

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

Khi bạn thực sự phải tải một tệp (không phải hình ảnh) từ kho lưu trữ JAR, bạn có thể thử điều này:

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!");
}

3
+1 Điều này làm việc cho tôi. Hãy chắc chắn rằng bạn đặt các tệp mà bạn muốn đọc trong binthư mục và đi đến thư mục của lớp đang tải tài nguyên trước khi sử dụng đường dẫn `/com/myorg/filename.ext '.
rayryeng

+1 Điều này cũng hiệu quả với tôi ... Tôi hiểu rằng có thể có một số rủi ro bảo mật liên quan đến phương pháp này, vì vậy chủ sở hữu ứng dụng cần phải nhận thức được điều đó.
LucasA

bạn có thể làm rõ tuyên bố này "Luôn luôn tốt hơn để tải tài nguyên với getResourceAsStream ()"? Làm thế nào điều này có thể cung cấp một giải pháp cho vấn đề?
Luca S.

@LucaS. Điều đó có nghĩa là chỉ đối với trường hợp hình ảnh, xin lỗi nó không rõ ràng. Cách tiếp cận nên độc lập với nền tảng, mặc dù đó là một cách khó khăn.
Tombart

@LucasA Bạn có thể làm rõ những rủi ro bạn đã đề cập?
ZX9

25

Câu trả lời một dòng là -

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

Về cơ bản getResourcephương pháp cho URL. Từ URL này, bạn có thể trích xuất đường dẫn bằng cách gọitoExternalForm()

Người giới thiệu:

getResource () , toExternalForm ()


7
Trong khi chạy từ môi trường của tôi (IntelliJ), điều này tạo ra một URL tệp đơn giản, hoạt động trong mọi trường hợp. Tuy nhiên, khi chạy từ chính jar, tôi nhận được một URI tương tự jar: file: /path/to/jar/jarname.jar! / File_in_jar.mp4. Không phải mọi thứ đều có thể sử dụng một URI bắt đầu bằng jar. Trường hợp tại điểm JavaFX Media.

1
Tôi thích câu trả lời này nhất. Cấp, trong hầu hết các trường hợp, có thể tốt hơn là chỉ cần lấy InputStream vào tài nguyên khi nó ở trong tệp jar, nhưng nếu vì lý do nào đó bạn thực sự cần đường dẫn, thì đây là cách hoạt động. Tôi cần đường dẫn để cung cấp cho đối tượng bên thứ ba. Vì vậy, cảm ơn!
Mario

1
giải pháp trên không hoạt động trong khi ở IDE, trong Intellijit thêm file:/vào đường dẫn, hoạt động trong jar nhưng không hoạt động trong IDE
Tayab Hussain

Phản hồi của bạn đã giải quyết một vấn đề mà tôi đã cố gắng giải quyết trong ít nhất 2 ngày nay. TYVM !!
Jonathan

12

Tôi đã dành một thời gian để loay hoay với vấn đề này, bởi vì không có giải pháp nào tôi thấy thực sự có hiệu quả, thật kỳ lạ! Thư mục làm việc thường không phải là thư mục của JAR, đặc biệt là nếu JAR (hoặc bất kỳ chương trình nào, cho vấn đề đó) được chạy từ Menu Start trong Windows. Vì vậy, đây là những gì tôi đã làm và nó hoạt động cho các tệp. Class chạy từ bên ngoài JAR cũng như nó hoạt động cho JAR. (Tôi chỉ thử nghiệm nó trong 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.
}

8

nếu netclient.pở trong tệp JAR, nó sẽ không có đường dẫn vì tệp đó nằm trong tệp khác. trong trường hợp đó, con đường tốt nhất bạn có thể có là thực sự file:/path/to/jarfile/bot.jar!/config/netclient.p.


Khi tôi thử và chuyển đổi một URL có định dạng này (... bot.jar! / Config / ...) sang URI, nó nói rằng đường dẫn không được phân cấp.
gEdringer

7

Bạn cần hiểu đường dẫn trong tệp jar.
Đơn giản chỉ cần tham khảo nó tương đối. Vì vậy, nếu bạn có một tệp (myfile.txt), nằm trong foo.jar trong \src\main\resourcesthư mục (kiểu maven). Bạn sẽ đề cập đến nó như:

src/main/resources/myfile.txt

Nếu bạn kết xuất jar của mình bằng cách sử dụng, jar -tvf myjar.jar bạn sẽ thấy đầu ra và đường dẫn tương đối trong tệp jar và sử dụng SLASH FORWARD.


Bạn thực sự phải sử dụng dấu gạch chéo về phía trước, ngay cả trên Windows. Điều đó ngụ ý rằng bạn không thể sử dụng File.separator.
str

3

Trong trường hợp của tôi, tôi đã sử dụng một đối tượng URL thay vì Đường dẫn.

Tập tin

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

Tài nguyên trong classpath sử dụng trình nạp lớp

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

Khi tôi cần đọc nội dung, tôi có thể sử dụng đoạn mã sau:

InputStream stream = url.openStream();

Và bạn có thể truy cập nội dung bằng InputStream.


3

Đây là cùng một mã từ người dùng Tombart với luồng phát và đóng để tránh sao chép nội dung tệp tạm thời không đầy đủ từ tài nguyên jar và để có tên tệp tạm thời duy nhất.

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());
}
         

2

Tệp là một bản tóm tắt cho một tệp trong hệ thống tệp và các hệ thống tệp không biết gì về nội dung của tệp JAR.

Hãy thử với một URI, tôi nghĩ rằng có một giao thức jar: // có thể hữu ích cho người bạn đời của bạn.



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

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Lấy cái này từ getSystemResourceAsStreamlà lựa chọn tốt nhất. Bằng cách lấy dòng đầu vào chứ không phải tệp hoặc URL, hoạt động trong tệp JAR và ở dạng độc lập.


1

Có thể hơi muộn nhưng bạn có thể sử dụng thư viện KResourceLoader của tôi để lấy tài nguyên từ jar của bạn:

File resource = getResource("file.txt")

0

Khi ở trong tệp jar, tài nguyên được đặt hoàn toàn trong phân cấp gói (không phải phân cấp hệ thống tệp). Vì vậy, nếu bạn có lớp com.example.Sweet đang tải một tài nguyên có tên "./default.conf" thì tên của tài nguyên được chỉ định là "/com/example/default.conf".

Nhưng nếu nó nằm trong một cái bình thì nó không phải là Tệp ...


0

Trong thư mục ressource của bạn (java / main / resource) trong tệp jar của bạn, hãy thêm tệp của bạn (chúng tôi giả sử rằng bạn đã thêm một tệp xml có tên là nhập tệp DOM ), sau đó bạn sẽ tiêm ResourceLoadernếu bạn sử dụng spring như dưới đây

@Autowired
private ResourceLoader resourceLoader;

bên trong chức năng tham quan viết mã dưới đây để tải tập tin:

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

0

Có lẽ phương pháp này có thể được sử dụng cho giải pháp nhanh chóng.

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;
    }
}

Bạn đang bỏ qua một số bối cảnh? Điều này mang lại cho tôi:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Allen Luce

Tôi đã chỉnh sửa câu trả lời và viết toàn bộ phương pháp mà tôi đã sử dụng trong một phần mềm
gbii

0

theo mã!

/ src / main / resource / 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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.