Я пишу программу Java, которая будет выполнять внешний файл ~/Java/exampleProject/bin/import.sh
. Моя программа находится в пакете gqqnbig
. Таким образом, структура каталогов
exampleProject/
bin/
import.sh
gqqnbig/
*.class
Когда я отлаживаю программу в eclipse, рабочий каталог ~/Java/exampleProject/
. Мне нужно выполнить bin/import.sh
.
Когда я запускаю программу в cmd, текущий каталог ~/Java/exampleProject/bin, мой код не найдет import.sh
.
Программа должна быть переносимой (распространять с import.sh). При правильной структуре каталогов она должна работать как на моем компьютере, так и на вашем компьютере, поэтому я не могу жестко закодировать путь import.sh.
Я также хочу упаковать его в один файл jar. Желаемая структура (рис. 1)
bin/
import.sh
program.jar
Итак, как моя программа может найти import.sh при запуске в eclipse, cmd и jar?
ОБНОВИТЬ
Я задаю свой вопрос по-другому. getAbsolutePath
функцию getAbsolutePath
, чтобы не работать в eclipse, в cmd или в файле jar в папке, которая также имеет import.sh (см. Рис. 1), результат идентичен.
public static void main(String[] args)
{
System.out.println("Look for "+getAbsolutePath()+"\\import.sh");
}
Здесь метод вытащил из одного из моих проектов. Он получает папку, в которой находится файл jar, в отличие от каталога, если он был запущен, если он вызывается в командной строке.
/**
* Retrieve a File representation of the folder this application is
* located in.
*
* @return
*/
private static File getApplicationRootFolder()
{
String path = FileGetter.class.getProtectionDomain().getCodeSource()
.getLocation().getPath();
try
{
String decodedPath = URLDecoder.decode(path, "UTF-8");
File jarParentFolder = new File(decodedPath).getParentFile();
if (jarParentFolder.exists() && jarParentFolder.canRead()
{
File shellScript = new File(jarParentFolder, "import.sh")
}
catch (UnsupportedEncodingException e)
{
Main.myLog.error(TAG, "Unencoding jar path failed on:\n\t" + path);
e.printStackTrace();
return null;
}
}
Затем вы можете использовать этот каталог для создания объекта File для вашего сценария оболочки. File shellScript = new File(getApplicationRootFolder(), scriptFilename);
EDIT: последующие вопросы, чтобы попытаться помочь вам и решение
Таким образом, вы хотите иметь доступ к одному файлу, который имеет три местоположения, в зависимости от того, когда/где вы запускаете код. Вот как я вижу эти случаи:
Случай 1: Выполняется непосредственно из Eclipse (неупакованный код):
shell script: X:/Java/exampleProject/bin/import.sh
class file: X:/Java/exampleProject/bin/gqqnbig/YourClass.class
Случай 2: Запуск упакованной банки (сценарий оболочки внутри):
shell script: X:/Java/YourJar.jar/bin/import.sh
class file: X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class
Случай 3: Запуск упакованной банки (внешний скрипт оболочки):
shell script: X:/Java/import.sh
class file: X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class
Я думаю, что вам нужно сделать так, чтобы определить порядок, в котором вы смотрите на эти места, и вернуться к следующему в строке, если сценарий оболочки не найден. Я думаю, вы хотите:
1. external to jar
2. inside packaged jar
3. unpackaged
Поэтому для доступа к ним вам нужно будет писать каждый отдельно и перемещаться по каждому, пока не получите File.exists() == true
.
Что-то вроде того, что следует. Примечание. Я не тестировал это, и возможны ошибки. Я оставлю вас разобраться с ними. Мой код основан на предположениях, сделанных выше, и я оставлю вас изменить код на основе любых неправильных догадок.
Итак, вот класс с одним открытым методом, принимающим аргумент имени файла и возвращающий InputStream. Я выбрал InputStream во всех случаях, так как как только вы упаковываете свою банку, вы больше не можете обращаться к ресурсам как к файловым объектам, а только к потокам.
public class FileGetter
{
private static String RESOURCE_DIRECTORY = "bin";
/**
* Retrieve an InputStream for a resource file.
*
* @param filename
* @return
*/
public InputStream getResourceFileStream(String filename)
{
// this is where you decide your preference or the priority of the locations
InputStream inputStream = null;
inputStream = getExternalFile(filename);
if (inputStream != null)
{
return inputStream;
}
inputStream = getInternalPackagedFile(filename);
if (inputStream != null)
{
return inputStream;
}
inputStream = getInternalUnpackagedFile(filename);
if (inputStream != null)
{
return inputStream;
}
// couldn't find the file anywhere so log some error or throw an exception
return null;
}
/**
* Retrieve an InputStream for a file located outside your Jar
*
* @param filename
* @return
*/
private static InputStream getExternalFile(String filename)
{
// get the jar absolute location on disk (regardless of current 'working directory')
String appRootPath = FileGetter.class.getProtectionDomain().getCodeSource()
.getLocation().getPath();
try
{
String decodedPath = URLDecoder.decode(appRootPath, "UTF-8");
File jarfile = new File(decodedPath);
File parentDirectory = jarfile.getParentFile();
if (testExists(parentDirectory))
{
File shellScript = new File(parentDirectory, filename);
if (testExists(shellScript))
{
return new FileInputStream(shellScript);
}
}
}
catch (UnsupportedEncodingException e)
{}
catch (NullPointerException e)
{}
catch (FileNotFoundException e)
{}
// if any part fails return null
return null;
}
/**
* Retrieve an InputStream for a file located inside your Jar.
*
* @param filename
* @return
*/
private static InputStream getInternalPackagedFile(String filename)
{
// root directory is defined as the jar root so we start with a "/".
URL resUrl = FileGetter.class.getResource(File.separator + RESOURCE_DIRECTORY
+ File.separator + filename);
String badPath = resUrl.getPath();
String goodPath = badPath.substring(badPath.indexOf("!") + 1);
InputStream input = FileGetter.class.getResourceAsStream(goodPath);
// returns null if nothing there so just
return input;
}
private static InputStream getInternalUnpackagedFile(String filename)
{
// eclipse will 'cd' to the code directory so we use relative paths
File shellScriptFile = new File(RESOURCE_DIRECTORY + File.separator + filename);
if (testExists(shellScriptFile))
{
try
{
InputStream shellScriptStream = new FileInputStream(shellScriptFile);
if (shellScriptStream != null)
{
return shellScriptStream;
}
}
catch (FileNotFoundException e)
{}
}
return null;
}
/**
* Test that a file exists and can be read.
*
* @param file
* @return
*/
private static boolean testExists(File file)
{
return file != null && file.exists() && file.canRead();
}
}
Но при всем том, что лучший способ сортировать это будет гарантировать, что файл существует на диске и создать его, если он не найден. Затем выполните скрипт с диска.
Я хотел бы получить окончательный ответ за это сам.
В качестве обходного пути я бы поставил 'import.sh' внутри exampleProject и изменил относительный путь к 'import.sh'.
В теории, которая должна работать внутри Eclipse и как пакетный Jar файл с program.jar и import.sh в том же каталоге.
К сожалению, он не будет работать в командной строке, возможно, кто-то может предложить лучший метод.
-Kaz
Я составил решение. Вызовите getExecutablePath(), чтобы получить унифицированный путь.
public static File getExecutablePath()
{
String workingDirectory = System.getProperty("user.dir");
File binFile = new File(workingDirectory, "bin");
if (binFile.exists() && (new File(workingDirectory, "src")).exists())
{
return binFile;
}
else if (isRunningFromJar())
return getApplicationRootFolder();
else
return new File(workingDirectory);
}
public static boolean isRunningFromJar()
{
String className = SystemHelper.class.getName().replace('.', '/');
String classJar = SystemHelper.class.getResource("/" + className + ".class").toString();
return classJar.startsWith("jar:");
}
/**
* Retrieve a File representation of the folder this application is located in.
*
* @return
*/
private static File getApplicationRootFolder()
{
try
{
String path = SystemHelper.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath;
decodedPath = URLDecoder.decode(path, "UTF-8");
File jarfile = new File(decodedPath);
return jarfile.getParentFile();
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
D:\Source Code\Java\PlayerStatisticReader\bin
, ваш метод возвращаетD:\Source Code\Java\PlayerStatisticReader
.D:\Source Code\Java\PlayerStatisticReader\bin\yourJar.jar
?