Đọc tệp thuộc tính bên ngoài tệp JAR


131

Tôi có một tệp JAR nơi tất cả mã của tôi được lưu trữ để chạy. Tôi phải truy cập một tệp thuộc tính cần được thay đổi / chỉnh sửa trước mỗi lần chạy. Tôi muốn giữ tệp thuộc tính trong cùng thư mục chứa tệp JAR. Có cách nào để bảo Java chọn tệp thuộc tính từ thư mục đó không?

Lưu ý: Tôi không muốn giữ tệp thuộc tính trong thư mục chính hoặc chuyển đường dẫn của tệp thuộc tính trong đối số dòng lệnh.


2
Xem câu trả lời này - "Thay vào đó lưu trữ tệp 'mặc định' bên trong Jar. Nếu nó bị thay đổi, hãy lưu trữ tệp đã thay đổi ở một nơi khác. Một vị trí phổ biến là thư mục con của user.home. Khi kiểm tra tệp, trước tiên hãy kiểm tra sự tồn tại của một tệp bị thay đổi trên hệ thống tệp và nếu nó không tồn tại, hãy tải tệp mặc định. " BTW "Tôi không muốn .." Những gì bạn muốn ít quan trọng hơn những gì hoạt động và thực tế. Lưu trữ ứng dụng. cài đặt trong thư mục ứng dụng bị cả Oracle & MS (và có thể là những người khác) không khuyến khích.
Andrew Thompson

3
Lý do tại sao tôi cần giữ tệp thuộc tính trong thư mục jar là tốt hơn là giữ chúng cùng nhau khi toàn bộ thư mục (bao gồm jar và thuộc tính) được sao chép sang máy khác và chạy.
Neil

Và nếu tôi buộc người dùng vượt qua đường dẫn tệp thuộc tính thì anh ta cần thay đổi nó mỗi khi anh ta chạy tệp bó từ một máy khác.
Neil

Câu trả lời:


144

Vì vậy, bạn muốn coi .propertiestệp của mình trên cùng một thư mục là jar chính / runnable như một tệp chứ không phải là tài nguyên của jar chính / runnable. Trong trường hợp đó, giải pháp của riêng tôi như sau:

Điều đầu tiên trước tiên: kiến ​​trúc tệp chương trình của bạn sẽ như thế này (giả sử chương trình chính của bạn là main.jar và tệp thuộc tính chính của nó là main.properies):

./ - the root of your program
 |__ main.jar
 |__ main.properties

Với kiến ​​trúc này, bạn có thể sửa đổi bất kỳ thuộc tính nào trong tệp main.properations bằng bất kỳ trình soạn thảo văn bản nào trước hoặc trong khi main.jar của bạn đang chạy (tùy thuộc vào trạng thái hiện tại của chương trình) vì đây chỉ là tệp dựa trên văn bản. Ví dụ: tệp main.properIES của bạn có thể chứa:

app.version=1.0.0.0
app.name=Hello

Vì vậy, khi bạn chạy chương trình chính của mình từ thư mục gốc / cơ sở, thông thường bạn sẽ chạy nó như thế này:

java -jar ./main.jar

hoặc, ngay lập tức:

java -jar main.jar

Trong tệp main.jar của bạn, bạn cần tạo một vài phương thức tiện ích cho mọi thuộc tính được tìm thấy trong tệp main.properIES của bạn; giả sử app.versiontài sản sẽ có getAppVersion()phương thức như sau:

/**
 * Gets the app.version property value from
 * the ./main.properties file of the base folder
 *
 * @return app.version string
 * @throws IOException
 */

import java.util.Properties;

public static String getAppVersion() throws IOException{

    String versionString = null;

    //to load application's properties, we use this class
    Properties mainProperties = new Properties();

    FileInputStream file;

    //the base folder is ./, the root of the main.properties file  
    String path = "./main.properties";

    //load the file handle for main.properties
    file = new FileInputStream(path);

    //load all the properties from this file
    mainProperties.load(file);

    //we have loaded the properties, so close the file handle
    file.close();

    //retrieve the property we are intrested, the app.version
    versionString = mainProperties.getProperty("app.version");

    return versionString;
}

Trong bất kỳ phần nào của chương trình chính cần app.versiongiá trị, chúng tôi gọi phương thức của nó như sau:

String version = null;
try{
     version = getAppVersion();
}
catch (IOException ioe){
    ioe.printStackTrace();
}

7
Giải pháp này hoạt động. Cảm ơn bạn đã hiểu chính xác yêu cầu và mã chi tiết. Tôi đã xác minh rằng tệp thuộc tính không nằm trong tệp jar nhưng vẫn có thể truy cập tệp từ cùng thư mục chứa tệp jar. Theo cách này, không yêu cầu mã cứng đường dẫn tuyệt đối. Cả tệp jar và thuộc tính bây giờ có thể được sao chép vào bất kỳ thư mục nào và chạy độc lập.
Neil

3
Tệp sẽ không được tìm thấy nếu bạn thực thi lệnh từ bên ngoài ví dụ: {{java -jar build / main.jar}}. Bạn có cách khắc phục nào không, @eee?
Dary

@Darian Không có gì để sửa ở đây; Nó chỉ hoạt động như được thiết kế trong đó tệp jar và tệp thuộc tính phải nằm trên cùng ./thư mục gốc (cùng cấp thư mục) như những gì tôi đã mô tả trong kiến ​​trúc tổ chức tệp. (theo yêu cầu được đặt bởi người đăng ban đầu)
ecle

@Darian, vì vậy nếu bạn muốn thực thi java -jar build/main.jar, bạn cũng cần đặt tệp thuộc tính vào buildthư mục để nó ở cùng cấp thư mục với tệp jar.
ecle

7
Cảm ơn phản hồi của bạn @eee, vấn đề là tôi không biết người dùng sẽ thực thi ở đâu java -jar path/to/jar/file. Nhưng tôi đã tìm thấy giải pháp trong một câu hỏi khác:String path = ClassLoader.getSystemClassLoader().getResource(".").getPath() + "/main.properties";
Dary

42

Tôi đã làm điều đó bằng cách khác.

Properties prop = new Properties();
    try {

        File jarPath=new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
        String propertiesPath=jarPath.getParentFile().getAbsolutePath();
        System.out.println(" propertiesPath-"+propertiesPath);
        prop.load(new FileInputStream(propertiesPath+"/importer.properties"));
    } catch (IOException e1) {
        e1.printStackTrace();
    }
  1. Nhận đường dẫn tệp Jar.
  2. Lấy thư mục Parent của tập tin đó.
  3. Sử dụng đường dẫn đó trong InputStreamPath với tên tệp thuộc tính của bạn.

Tôi đã phải bỏ phần getParentFile () để thay vào đó tôi đã sử dụng: String propertyPath = jarPath.getAbsolutePath (); nhưng tất cả phụ thuộc vào vị trí của tệp
MobileMon

4
Chỉ cần thay thế "jarPath.getParentFile (). GetAbsolutePath ();" thành "jarPath.getParent ()". Làm việc như một cơ duyên rồi.
StackAddict

1
Tương tự là vấn đề trong trường hợp của tôi nhưng tôi có dự án cơ sở mùa xuân. Làm thế nào để nói với mùa xuân rằng tập tin bên cạnh tập tin jar? bất kỳ ý tưởng
Mubasher

3

Luôn có vấn đề khi truy cập các tệp trên thư mục tệp của bạn từ tệp jar. Cung cấp classpath trong một tệp jar là rất hạn chế. Thay vào đó hãy thử sử dụng tệp bat hoặc tệp sh để bắt đầu chương trình của bạn. Theo cách đó, bạn có thể chỉ định đường dẫn lớp của mình theo cách bạn muốn, tham khảo bất kỳ thư mục nào ở bất kỳ đâu trên hệ thống.

Cũng kiểm tra câu trả lời của tôi về câu hỏi này:

tạo tập tin .exe cho dự án java có chứa sqlite


1

Tôi có một trường hợp tương tự: muốn *.jartập tin của tôi truy cập một tập tin trong một thư mục bên cạnh *.jartập tin đã nói . Tham khảo TRẢ LỜI NÀY là tốt.

Cấu trúc tập tin của tôi là:

./ - the root of your program
|__ *.jar
|__ dir-next-to-jar/some.txt

Tôi có thể tải một tệp (giả sử some.txt) vào InputStream bên trong *.jartệp bằng cách sau:

InputStream stream = null;
    try{
        stream = ThisClassName.class.getClass().getResourceAsStream("/dir-next-to-jar/some.txt");
    }
    catch(Exception e) {
        System.out.print("error file to stream: ");
        System.out.println(e.getMessage());
    }

Sau đó, làm bất cứ điều gì bạn sẽ với stream


0

Tôi có một ví dụ về việc thực hiện cả hai bằng classpath hoặc từ cấu hình bên ngoài với log4j2.properies

package org.mmartin.app1;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.LogManager;


public class App1 {
    private static Logger logger=null; 
    private static final String LOG_PROPERTIES_FILE = "config/log4j2.properties";
    private static final String  CONFIG_PROPERTIES_FILE = "config/config.properties";

    private Properties properties= new Properties();

    public App1() {
        System.out.println("--Logger intialized with classpath properties file--");
        intializeLogger1();
        testLogging();
        System.out.println("--Logger intialized with external file--");
        intializeLogger2();
        testLogging();
    }




    public void readProperties()  {
        InputStream input = null;
        try {
            input = new FileInputStream(CONFIG_PROPERTIES_FILE);
            this.properties.load(input);
        } catch (IOException e) {
            logger.error("Unable to read the config.properties file.",e);
            System.exit(1);
        }
    }

    public void printProperties() {
        this.properties.list(System.out);
    }

    public void testLogging() {
        logger.debug("This is a debug message");
        logger.info("This is an info message");
        logger.warn("This is a warn message");
        logger.error("This is an error message");
        logger.fatal("This is a fatal message");
        logger.info("Logger's name: "+logger.getName());
    }


    private void intializeLogger1() {
        logger = LogManager.getLogger(App1.class);
    }
    private void intializeLogger2() {
        LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
        File file = new File(LOG_PROPERTIES_FILE);
        // this will force a reconfiguration
        context.setConfigLocation(file.toURI());
        logger = context.getLogger(App1.class.getName());
    }

    public static void main(String[] args) {
        App1 app1 = new App1();
        app1.readProperties();
        app1.printProperties();
    }
}


--Logger intialized with classpath properties file--
[DEBUG] 2018-08-27 10:35:14.510 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.513 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.513 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.513 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.513 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.514 [main] App1 - Logger's name: org.mmartin.app1.App1
--Logger intialized with external file--
[DEBUG] 2018-08-27 10:35:14.524 [main] App1 - This is a debug message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - This is an info message
[WARN ] 2018-08-27 10:35:14.525 [main] App1 - This is a warn message
[ERROR] 2018-08-27 10:35:14.525 [main] App1 - This is an error message
[FATAL] 2018-08-27 10:35:14.525 [main] App1 - This is a fatal message
[INFO ] 2018-08-27 10:35:14.525 [main] App1 - Logger's name: org.mmartin.app1.App1
-- listing properties --
dbpassword=password
database=localhost
dbuser=user

0

Điều này làm việc cho tôi. Tải tệp thuộc tính của bạn từcurrent directory

Properties properties = new Properties();
properties.load(new FileReader(new File(".").getCanonicalPath() + File.separator + "java.properties"));
properties.forEach((k, v) -> {
            System.out.println(k + " : " + v);
        });

Hãy chắc chắn rằng, đó java.propertieslà tại current directory. Bạn chỉ có thể viết một đoạn script khởi động nhỏ chuyển sang thư mục bên phải trước đó, như

#! /bin/bash
scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 
cd $scriptdir
java -jar MyExecutable.jar
cd -

Trong dự án của bạn chỉ cần đặt java.propertiestệp vào gốc dự án của bạn, để làm cho mã này hoạt động từ IDE của bạn.


0

Ở đây nếu bạn đề cập .getPath()thì điều đó sẽ trả về đường dẫn của Jar và tôi đoán bạn sẽ cần cha mẹ của nó để tham khảo tất cả các tệp cấu hình khác được đặt cùng với tệp jar. Mã này hoạt động trên Windows. Thêm mã trong lớp chính.

File jarDir = new File(MyAppName.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String jarDirpath = jarDir.getParent();

System.out.println(jarDirpath);
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.