Как получить путь к ресурсу в файле JAR Java
Я пытаюсь найти путь к ресурсу, но мне не повезло.
Это работает (как в 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)
Есть ли способ получить путь к файлу ресурса?
18 ответов
Это намеренно. Содержимое "файла" может быть недоступно в виде файла. Помните, что вы имеете дело с классами и ресурсами, которые могут быть частью файла 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.toString().startsWith("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);
}
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, особенно если JAR (или любая другая программа) запускается из меню "Пуск" в Windows. Итак, вот что я сделал, и он работает для файлов.class, запускаемых извне JAR, так же, как и для 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), расположенный в foo.jar под \src\main\resources
каталог (стиль мавен). Вы бы ссылались на это как:
src/main/resources/myfile.txt
Если вы бросите свою банку, используя jar -tvf myjar.jar
вы увидите выходные данные и относительный путь в файле jar, и будете использовать FORWARD SLASHES.
В моем случае я использовал объект URL вместо Path.
файл
File file = new File("my_path");
URL url = file.toURI().toURL();
Ресурс в classpath с помощью загрузчика классов
URL url = MyClass.class.getClassLoader().getResource("resource_name")
Когда мне нужно прочитать содержимое, я могу использовать следующий код:
InputStream stream = url.openStream();
И вы можете получить доступ к содержимому с помощью InputStream.
Следовать коду!
/ SRC / основные / ресурсы / файл
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;
}
}
Файл - это абстракция для файла в файловой системе, и файловые системы ничего не знают о том, что является содержимым JAR.
Попробуйте использовать URI, я думаю, что есть протокол jar://, который может быть полезен для ваших целей.
private static final String FILE_LOCATION = "com/input/file/somefile.txt";
//Method Body
InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);
Получение этого от getSystemResourceAsStream
это лучший вариант. Получая входной поток, а не файл или URL, работает в JAR-файле и как отдельный.
Это может быть немного поздно, но вы можете использовать мою библиотеку KResourceLoader, чтобы получить ресурс из вашего jar:
File resource = getResource("file.txt")
Это, как уже было сказано, не оптимальный способ загрузки ресурсов, но если вам абсолютно необходимо иметь java.io.File
ссылка, затем попробуйте следующее:
URL url = null;
try {
URL baseUrl = YourClass.class.getResource(".");
if (baseUrl != null) {
url = new URL(baseUrl, "yourfilename.ext");
} else {
url = YourClass.class.getResource("yourfilename.ext");
}
} catch (MalformedURLException e) {
// Do something appropriate
}
Это дает вам java.net.URL
и это может быть использовано в java.io.File
конструктор.
Следующий путь работал для меня: classpath:/path/to/resource/in/jar
В вашей папке ресурсов (java/main/resources) вашего jar добавьте свой файл (мы предполагаем, что вы добавили XML-файл с именем import.xml), после чего вы вводите ResourceLoader
если вы используете весну, как показано ниже
@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();}
Эта функция класса может получить абсолютный путь к файлу по относительному пути к файлу в файле .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
.
Находясь в файле JAR, ресурс находится абсолютно в иерархии пакетов (не в иерархии файловой системы). Поэтому, если у вас есть класс com.example.Sweet, загружающий ресурс с именем "./default.conf", тогда имя ресурса указывается как "/com/example/default.conf".
Но если это в банке, то это не файл...
Может быть, этот метод может быть использован для быстрого решения.
public static String getResourcePath(String relativePath)
{
String pathStr = null;
URL location = <Class>.class.getProtectionDomain().getCodeSource().getLocation();
String codeLoaction = location.toString();
try{
if (codeLocation.endsWith(".jar"){
//Call from jar
Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
pathStr = path.toString();
}else{
//Call from IDE
pathStr = <Class>.class.getClassLoader().getResource(relativePath).getPath();
}
}catch(URISyntaxException ex){
ex.printStackTrace();
}
return pathStr;
}