Я разработал приложение JavaFX Todo, которое считывает и записывает задачи в файл CSV. Приложение отлично работает в моей среде разработки (IntelliJ). Однако у меня возникают проблемы при экспорте приложения в виде файла JAR и, в конечном итоге, преобразовании его в файл EXE для распространения.
Когда я запускаю файл JAR, приложение не может найти файл CSV. Я не уверен, как правильно упаковать файл CSV с помощью JAR или как гарантировать, что приложение сможет читать и записывать файл CSV после его экспорта.
Как мне структурировать свой проект и настроить класс FileService
, чтобы файл CSV был доступен при экспорте приложения в виде JAR?
Существует ли рекомендуемый способ обработки путей к файлам для таких ресурсов, как файлы CSV, при распространении приложения JavaFX в виде JAR или EXE?
Файл CSV в настоящее время находится в каталоге Resources/Database/ в структуре моего проекта. Я планирую преобразовать JAR в EXE-файл для облегчения распространения среди друзей.
Будем очень признательны за любые советы по правильному подходу или передовому опыту!
.
└── src/
├── Controller/
│ ├── TodoController
│ └── TodoItemController
├── Model/
│ └── Status
├── View/
│ ├── TodoItem.fxml
│ └── TodoManager.fxml
├── Util/
│ └── AlerUtil
├── Service/
│ └── TodoManager
└── Resources/
├── Database/
│ └── tasks.csv
├── bin.png
├── plus.png
└── logo.png
package Service;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class FileService {
private final String filePath = "src/Resources/Database/tasks.csv";
/**
* @return List of String Array representing rows and columns
*/
public List<String[]> readCsv() {
List<String[]> data = new ArrayList<>();
try (CSVReader reader = new CSVReader(new FileReader(filePath))) {
// skip headers row 1.
reader.skip(1);
// read the rest of the rows
data = reader.readAll();
} catch (FileNotFoundException e) {
System.err.println("Unable to locate task.csv " + e);
} catch (IOException | CsvException e) {
System.err.println(e.getMessage());
}
return data;
}
public void appendData(String[] record) {
// passing true as the second parameter for append
try (CSVWriter writer = new CSVWriter(new FileWriter(filePath, true))) {
// appending the record
writer.writeNext(record);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public void writeData(List<String[]> data) {
try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) {
// write the header
writer.writeNext(new String[] {"task", "dateCreated", "status", "dateCompleted"});
// write the data
writer.writeAll(data);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
Я создал файл JAR с помощью IntelliJ.
Я попытался сделать файл CSV доступным, поместив его в папку на рабочем столе и обновив класс FileService, чтобы он указывал на этот путь к файлу.
Я экспортировал приложение в эту папку, но приложению не удалось получить доступ к CSV-файлу при запуске из JAR.
Как указано в другом комментарии, ресурсы и вообще все, что входит в файл jar, по существу доступны только для чтения. Если предполагается, что файл будет изменен и доступен при нескольких вызовах вашего приложения, я бы рекомендовал поместить его в невременное «хорошо известное место» (например, в домашний каталог пользователя). Некоторые примеры см. на странице stackoverflow.com/questions/73927071/….
Чтобы прочитать файл, вы не можете использовать локальный путь к файлу (src/main/resources/xxx), вам нужно использовать что-то вроде: var cl = Thread.currentThread().getContextClassLoader(); var stream = cl.getResourceAsStream("Database/tasks.csv");
и читать из InputStream. Но написать на него, как говорилось в предыдущих комментариях, вы не сможете.
Еще один пример здесь
Чтобы прочитать файл, вам нужно что-то вроде new CSVReader(new InputStreamReader(getClass().getResourceAsStream("/Database/tasks.csv")))
, но, как уже отмечалось, вы не сможете в него писать. Дополнительную информацию о поиске ресурсов см. stackoverflow.com/questions/61531317/…
Как мне структурировать свой проект
Вы должны следовать стандартной структуре каталогов maven, используя инструмент сборки (я рекомендую Maven, но при желании вы можете использовать Gradle). Хотя вы можете использовать нестандартный макет, редко бывает веская причина для этого, за исключением случаев интеграции очень большого устаревшего проекта в условиях ограниченных временных ограничений.
Поместите свой код под:
src/main/java
и ваши ресурсы только для чтения в разделе:
src/main/resources
Мастер нового проекта JavaFX Idea создаст для вас рабочий проект с этой структурой, настроив инструмент сборки по вашему выбору. Мастер предоставит образец файла FXML и его использование, но вы можете удалить его, если не используете FXML.
Загрузка происходит по шаблону:
Что обычно будет:
URL resourceUrl = SomeClass.class.getResource(location);
ИЛИ
InputStream resourceStream = SomeClass.class.getResourceAsStream(location);
Где местоположение представляет собой строку со следующими свойствами:
/
для абсолютного местоположения ИЛИ с префиксом без префикса для относительного местоположения для SomeClass
..
навигация по родительскому пути не используется.src
никогда не включается в строку местоположения.Полное руководство находится по адресу:
Однако вы хотите иметь возможность также записывать в свой файл данных. Таким образом, ресурс только для чтения либо бесполезен, либо полезен только для предоставления исходных данных по умолчанию для копирования в другое место (например, в файл в файловой системе или в хранилище базы данных отдельно от приложения).
Учебное пособие Makery JavaFX содержит информацию о механизме хранения данных для чтения/записи. Он использует формат хранения XML, но CSV также подойдет.
Примеры копирования данных ресурсов, доступных только для чтения по умолчанию, в доступное для записи место (если вам это нужно):
Распространение — сложная тема, но создать zip-архив jlink легко . jpackage сложнее, но при необходимости можно создать установщик. Дополнительную информацию можно найти в разделе упаковки под тегом JavaFX.
Насколько мне известно, файлы внутри JAR не могут быть обновлены. Вероятно, вам потребуется извлечь файл из JAR в какое-нибудь известное место, доступное на каждом компьютере, где установлен ваш JAR, например путь, возвращаемый
System.getProperty("java.io.tmpdir");