อ่านไฟล์คุณสมบัติภายนอกไฟล์ JAR


131

ฉันมีไฟล์ JAR ซึ่งรหัสทั้งหมดของฉันถูกเก็บถาวรเพื่อใช้งาน ฉันต้องเข้าถึงไฟล์คุณสมบัติที่ต้องเปลี่ยนแปลง / แก้ไขก่อนการรันแต่ละครั้ง ฉันต้องการเก็บไฟล์คุณสมบัติไว้ในไดเร็กทอรีเดียวกันกับที่ไฟล์ JAR อยู่ มีการแจ้งให้ Java รับไฟล์คุณสมบัติจากไดเร็กทอรีนั้นหรือไม่?

หมายเหตุ: ฉันไม่ต้องการเก็บไฟล์คุณสมบัติไว้ในโฮมไดเร็กทอรีหรือส่งเส้นทางของไฟล์คุณสมบัติในอาร์กิวเมนต์บรรทัดคำสั่ง


2
ดูคำตอบนี้ - "แทนที่จะเก็บไฟล์" ค่าเริ่มต้น "ไว้ใน Jar หากมีการเปลี่ยนแปลงให้เก็บไฟล์ที่เปลี่ยนแปลงไว้ในที่อื่นสถานที่ทั่วไปหนึ่งคือไดเร็กทอรีย่อยของuser.homeเมื่อตรวจสอบไฟล์ก่อนอื่นให้ตรวจสอบการมีอยู่ของ ไฟล์ที่เปลี่ยนแปลงในระบบไฟล์และหากไม่มีอยู่ให้โหลดไฟล์เริ่มต้น " BTW "ฉันไม่ต้องการ .. "สิ่งที่คุณต้องการมีความสำคัญน้อยกว่าสิ่งที่ได้ผลและใช้ได้จริง กำลังจัดเก็บแอป การตั้งค่าในไดเร็กทอรีแอปพลิเคชันไม่ได้รับการสนับสนุนอย่างยิ่งจากทั้ง Oracle & MS (และอาจเป็นอย่างอื่น)
Andrew Thompson

3
เหตุผลที่ฉันต้องเก็บไฟล์คุณสมบัติไว้ในไดเร็กทอรี jar คือควรเก็บไฟล์เหล่านี้ไว้ด้วยกันเมื่อทั้งไดเร็กทอรี (รวมถึง jar และคุณสมบัติ) ถูกคัดลอกไปยังเครื่องอื่นและเรียกใช้
Neil

และถ้าฉันบังคับให้ผู้ใช้ส่งเส้นทางไฟล์คุณสมบัติเขาก็ต้องเปลี่ยนทุกครั้งที่เรียกใช้ไฟล์แบตช์จากเครื่องอื่น
Neil

คำตอบ:


144

ดังนั้นคุณต้องการจัดการ.propertiesไฟล์ของคุณในโฟลเดอร์เดียวกันกับ jar หลัก / runnable เป็นไฟล์แทนที่จะเป็นทรัพยากรของ jar หลัก / runnable ในกรณีนั้นวิธีแก้ปัญหาของฉันเองมีดังนี้:

สิ่งแรกแรก: สถาปัตยกรรมไฟล์โปรแกรมของคุณจะต้องเป็นแบบนี้ (สมมติว่าโปรแกรมหลักของคุณคือ main.jar และไฟล์คุณสมบัติหลักคือ main.properties):

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

ด้วยสถาปัตยกรรมนี้คุณสามารถแก้ไขคุณสมบัติใด ๆ ในไฟล์ main.properties โดยใช้โปรแกรมแก้ไขข้อความใด ๆ ก่อนหรือในขณะที่ main.jar ของคุณกำลังทำงานอยู่ (ขึ้นอยู่กับสถานะปัจจุบันของโปรแกรม) เนื่องจากเป็นเพียงไฟล์แบบข้อความ ตัวอย่างเช่นไฟล์ main.properties ของคุณอาจมี:

app.version=1.0.0.0
app.name=Hello

ดังนั้นเมื่อคุณเรียกใช้โปรแกรมหลักจากโฟลเดอร์รูท / ฐานโดยปกติคุณจะเรียกใช้งานในลักษณะนี้:

java -jar ./main.jar

หรือทันที:

java -jar main.jar

ใน main.jar ของคุณคุณต้องสร้างเมธอดยูทิลิตี้สองสามอย่างสำหรับทุกคุณสมบัติที่พบในไฟล์ main.properties ของคุณ สมมติว่าapp.versionคุณสมบัติจะมีgetAppVersion()วิธีการดังนี้:

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

ในส่วนใด ๆ ของโปรแกรมหลักที่ต้องการapp.versionค่าเราเรียกวิธีการดังต่อไปนี้:

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

7
วิธีนี้ใช้ได้ผล ขอบคุณที่เข้าใจข้อกำหนดที่แน่นอนและรหัสโดยละเอียดฉันตรวจสอบแล้วว่าไฟล์คุณสมบัติไม่ได้อยู่ในไฟล์ jar แต่ก็ยังสามารถเข้าถึงไฟล์จากไดเร็กทอรีเดียวกับที่ไฟล์ jar อยู่ ด้วยวิธีนี้จึงไม่จำเป็นต้องใช้รหัสฮาร์ดพา ธ สัมบูรณ์ ตอนนี้ทั้งไฟล์ jar และคุณสมบัติสามารถคัดลอกไปยังไดเร็กทอรีใดก็ได้และเรียกใช้อย่างอิสระ
Neil

3
จะไม่พบไฟล์หากคุณรันคำสั่งจากภายนอกสำหรับ ex: {{java -jar build / main.jar}} คุณมีวิธีแก้ไขหรือไม่ @eee?
Darian

@Darian ไม่มีอะไรต้องแก้ไขที่นี่; ใช้งานได้ตามที่ออกแบบไว้เท่านั้นโดยที่ jar และไฟล์คุณสมบัติต้องอยู่ใน./โฟลเดอร์รูทเดียวกัน (ระดับไดเร็กทอรีเดียวกัน) ตามที่ฉันได้อธิบายไว้ในสถาปัตยกรรมการจัดระเบียบไฟล์ (ตามความต้องการที่กำหนดโดยผู้โพสต์ต้นฉบับ)
ecle

@Darian ดังนั้นหากคุณต้องการเรียกใช้java -jar build/main.jarงานคุณต้องใส่ไฟล์คุณสมบัติไว้ในbuildโฟลเดอร์ด้วยเพื่อให้อยู่ในระดับไดเร็กทอรีเดียวกับ jar
ecle

7
ขอบคุณสำหรับการตอบกลับของคุณ @eee ปัญหาคือฉันไม่รู้ว่าผู้ใช้จะดำเนินการไฟล์java -jar path/to/jar/file. แต่ฉันพบวิธีแก้ปัญหาในคำถามอื่น:String path = ClassLoader.getSystemClassLoader().getResource(".").getPath() + "/main.properties";
Darian

42

ฉันทำโดยวิธีอื่น

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. รับเส้นทางไฟล์ Jar
  2. รับโฟลเดอร์หลักของไฟล์นั้น
  3. ใช้เส้นทางนั้นใน InputStreamPath ด้วยชื่อไฟล์คุณสมบัติของคุณ

ฉันต้องทิ้งส่วน getParentFile () ดังนั้นฉันจึงใช้: String propertiesPath = jarPath.getAbsolutePath (); แต่ทุกอย่างขึ้นอยู่กับตำแหน่งของไฟล์
MobileMon

4
เพียงแค่แทนที่ "jarPath.getParentFile (). getAbsolutePath ();" เป็น "jarPath.getParent ()" ใช้งานได้เหมือนมีเสน่ห์แล้ว
StackAddict

1
ปัญหาเดียวกันในกรณีของฉัน แต่ฉันมีโครงการฐานสปริง จะบอกสปริงว่าไฟล์อยู่ข้างไฟล์ jar ได้อย่างไร? ความคิดใด ๆ
Mubasher

3

มักมีปัญหาในการเข้าถึงไฟล์ในไดเร็กทอรีไฟล์ของคุณจากไฟล์ jar การจัดเตรียม classpath ในไฟล์ jar นั้นมีข้อ จำกัด มาก ลองใช้ไฟล์ bat หรือไฟล์ sh เพื่อเริ่มโปรแกรมของคุณแทน ด้วยวิธีนี้คุณสามารถระบุ classpath ของคุณได้ตามที่คุณต้องการโดยอ้างอิงโฟลเดอร์ใดก็ได้ในระบบ

ตรวจสอบคำตอบของฉันสำหรับคำถามนี้ด้วย:

การสร้างไฟล์. exe สำหรับโปรเจ็กต์ java ที่มี sqlite


1

ฉันมีกรณีที่คล้ายกัน: ต้องการ*.jarให้ไฟล์ของฉันเข้าถึงไฟล์ในไดเร็กทอรีถัดจาก*.jarไฟล์ดังกล่าว อ้างถึงคำตอบนี้เช่นกัน

โครงสร้างไฟล์ของฉันคือ:

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

ฉันสามารถโหลดไฟล์ (พูดsome.txt) ไปยัง InputStream ภายใน*.jarไฟล์ได้ดังต่อไปนี้:

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

จากนั้นทำทุกอย่างที่คุณต้องการด้วยไฟล์ stream


0

ฉันมีตัวอย่างของการทำทั้งสองอย่างโดย classpath หรือจากการกำหนดค่าภายนอกด้วย log4j2.properties

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

สิ่งนี้ใช้ได้กับฉัน โหลดไฟล์คุณสมบัติของคุณจาก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);
        });

ตรวจสอบให้แน่ใจว่าjava.propertiesอยู่ที่ไฟล์current directory. คุณสามารถเขียนสคริปต์เริ่มต้นเล็กน้อยที่สลับไปยังไดเร็กทอรีที่ถูกต้องในก่อนหน้านี้เช่น

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

ในโปรเจ็กต์ของคุณเพียงแค่ใส่java.propertiesไฟล์ในรูทโปรเจ็กต์ของคุณเพื่อให้โค้ดนี้ทำงานจาก IDE ของคุณ


0

ที่นี่ถ้าคุณพูดถึง.getPath()นั่นจะส่งคืนเส้นทางของ Jar และฉันเดาว่าคุณจะต้องใช้พาเรนต์เพื่ออ้างถึงไฟล์ config อื่น ๆ ทั้งหมดที่วางไว้ใน jar รหัสนี้ใช้ได้กับ Windows เพิ่มรหัสภายในคลาสหลัก

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

System.out.println(jarDirpath);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.