I have to implement a temporary "persistence" solution for retrieving some definitions ( simple json strings). I have a rest endpoint which creates an instance of my object and then I want to write the json definition inside a file.
I currently have a class loader which loads files from the resources folder inside my module, I use these to grab the query results. Now I want to save a new definition, and write it to a file inside the resources folder.
However, I am unsuccessful in doing so. I grab the path from the existing file with the classloader and when I try to create a new file using FileUtils, it either throws some errors, or creates the file in D:.
Is there a way to use the resources folder to add/edit files at runtime ? Here is some code also with one of the many ways i've tried it.
public String writeDocument(String path, String content) throws IOException {
\\Example call: writeDocument("/definitions/somedefinition.txt",jsonStringHere);
\\where /definitions/somedefinition.txt is placed in /resources
ClassLoader classLoader = getClass().getClassLoader();
final URL url = classLoader.getResource(path);
final File file;
String finalPath = null;
try {
file = Paths.get(url.toURI()).toFile();
FileUtils.writeStringToFile(file, content);
finalPath = file.getAbsolutePath();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return finalPath;
}
Related
I have deployed a spring-boot application JAR file. Now, I want to upload the image from android and store it in the myfolder of resource directory. But unable to get the path of resource directory.
Error is:
java.io.FileNotFoundException: src/main/resources/static/myfolder/myimage.png
(No such file or directory)
This is the code for storing the file in the resource folder
private final String RESOURCE_PATH = "src/main/resources";
String filepath = "/myfolder/";
public String saveFile(byte[] bytes, String filepath, String filename) throws MalformedURLException, IOException {
File file = new File(RESOURCE_PATH + filepath + filename);
OutputStream out = new FileOutputStream(file);
try {
out.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
out.close();
}
return file.getName();
}
UPDATED:
This is what I have tried
private final String RESOURCE_PATH = "config/";
controller class:
String filepath = "myfolder/";
String filename = "newfile.png"
public String saveFile(byte[] bytes, String filepath, String filename) throws MalformedURLException, IOException {
//reading old file
System.out.println(Files.readAllBytes(Paths.get("config","myfolder","oldfile.png"))); //gives noSuchFileException
//writing new file
File file = new File(RESOURCE_PATH + filepath + filename);
OutputStream out = new FileOutputStream(file); //FileNotFoundException
try {
out.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
out.close();
}
return file.getName();
}
Project structure:
+springdemo-0.0.1-application.zip
+config
+myfolder
-oldfile.png
-application.properties
+lib
+springdemo-0.0.1.jar
+start.sh
-springdemo-0.0.1.jar //running this jar file
Usually when you deploy an application (or start it using Java), you start a JAR file. You don't have a resource folder. You can have one and access it, too, but it certainly won't be src/main/resources.
When you build your final artifact (your application), it creates a JAR (or EAR or WAR) file and your resources, which you had in your src/main/resources-folder, are copied over to the output directory and included in the final artifact. That folder simply does not exist when the application is run (assuming you are trying to run it standalone).
During the build process target/ is created and contains the classes, resources, test-resources and the likes (assuming you are building with Maven; it is a little different if you build using Gradle or Ant or by hand).
What you can do is create a folder e.g. docs next to your final artifact, give it the appropriate permissions (chmod/chown) and have your application output files into that folder. This folder is then expected to exist on the target machine running your artifact, too, so if it doesn't, it would mean the folder does not exist or the application lacks the proper permissions to read from / write to that folder.
If you need more details, don't hesitate to ask.
Update:
To access a resource, which is bundled and hence inside your artifact (e.g. final.jar), you should be able to retrieve it by using e.g. the following:
testText = new String(ControllerClass.class.getResourceAsStream("/test.txt").readAllBytes());
This is assuming your test.txt file is right under src/main/resources and was bundled to be directly in the root of your JAR-file (or target folder where your application is run from). ControllerClass is the controller, which is accessing the file. readAllBytes just does exactly this: read all the bytes from a text file. For accessing images inside your artifact, you might want to use ImageIO.
IF you however want to access an external file, which is not bundled and hence not inside your artifact, you may use File image = new File(...) where ... would be something like "docs/image.png". This would require you to create a folder called docs next to your JAR-artifact and put a file image.png inside of it.
You of course also may work with streams and there are various helpful libraries for working with input- and output streams.
The following was meant for AWT, but it works in case you really want to access the bytes of your image: ImageIO. In a controller you usually wouldn't want to do that, but rather have your users access (and thus download) it from a given available folder.
I hope this helps :).
I am trying to load and validate xml files from a directory in the class path at startup of a Spring Boot application. I am seeing the following error which indicates that I am trying to load files using absolute path and not class path:
java.io.FileNotFoundException: class path resource [converters/mapper.xml] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/opt/core/home/libexec/boss/core-service-2.0.0.jar!/BOOT-INF/lib/core-api-2.0.0.jar!/converters/mapper.xml
Below is a code snippet that loads the files:
..
#Autowired
public FieldsMapTypeConvertersRegistry(#Value("${core.files-location:converters}")
String mapperFilesLocation) {
this.mapperFilesLocation = mapperFilesLocation;
}
..
try {
// ToDo we need to replace this when we enable multi-tenancy
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
Resource[] xmlResources = resolver.getResources(mapperFilesLocation + "/*.xml");
for (Resource xmlResource : xmlResources) {
File file = ResourceUtils.getFile(xmlResource.getURL());
registerTypeConverter(file);
}
} catch (IOException e) {
// do stuff
} catch (JAXBException e) {
//do stuff
}
I think the issue is in this statement in the code above:
File file = ResourceUtils.getFile(xmlResource.getURL());
but I am not sure what other ways I can do that. Any help is really appreciated.
I'm just wondering why you are using ResourceUtils.getFile(xmlResource.getURL()) when xmlResource.getFile() is already available to get the File Handle. Ideally speaking, you should be catching the FileNotFoundException inside the catch block and checking the detailed message wrapped inside the exception.
Edit:
The exception is being thrown because the xml is not found in classpath at runtime. Most probably, the file target/converters/mapper.xml is not available.
Try something like this MyService.class.getClassLoader().getResourceAsStream("/file.xml"); and then create File from stream.
Try using commons-io:commons-io:2.7 (Maven artifact) and use the following code:
InputStream inputStream = obj.getClass()
.getClassLoader()
.getResourceAsStream("converters/mapper.xml");
String data = IOUtils.toString(inputStream, "UTF-8");
So I have a small resource loader for stuff that I need. The jar packages the resources but when I build the maven project, and all the dependencies work and resources folder is marked as resources, my images and scripts wont load.
Here is the code I am using:
...
public class ResourceLoader
{
public static ImageIcon getImageIconResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
File file = new File(classLoader.getResource("img/" + fileName).getFile());
return new ImageIcon(file.getPath());
}
public static File getScriptResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
return new File(classLoader.getResource("scripts/" + fileName).getFile());
}
}
The problem is not with maven itself. When you call
File.getPath
on File from the resource it points to a File, that is actually inside of your application archive. For most applications, this pose a problem because you cannot read file without extracting the archive. To correctly use resource file, you have to work with File, or you can call
ClassLoader.getResourcesAsStream
To adress ImageIcon
public static ImageIcon getImageIconResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream is = classLoader.getResourceAsStream("img/" + fileName);
Image image = ImageIO.read(is);
return new ImageIcon(image);
}
As for your getScriptResource method geting File object should work. But that depends, on how you will later use it. As I think you will need to read it anyway at some point I suggest using input stream as well.
public static InpoutStream getScriptResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream is = classLoader.getResourceAsStream("scripts/" + fileName);
return is;
}
Then, you can read the InputStream using many options that suits your need. For example, you can take a look at Apache Commons' IoUtils or handle it using ReaderApi
EDIT:
Because you have clarified how you will use your file I can see where is the problem with your scripts. You are starting another process outside of your application. In the first CLI param of python3, you are providing path to the file. As I wrote earlier - this is the problem, because python3 cannot read file inside of .jar file. First of all, I would have questioned your architecture. Do you really need to have script inside of .jar?
Anyway, one possible workaround may be storing contents of a script File in temporaryFile.
File tempFile = File.createTempFile("prefix-", "-suffix");
// e.g.: File tempFile = File.createTempFile("MyAppName-", ".tmp");
tempFile.deleteOnExit();
//get your script and prepare OutputStream to tempFile
// Try with resources, because you want to close your streams
try (InputStream is = ResourceLoader.getScriptResource(scriptName);
FileOutputStream out = new FileOutputStream(tempFile)) {
//NOTE: You can use any method to copy InputStream to OutputStream.
//Here I have used Apache IO Utils
IOUtils.copy(is, out);
}
boolean success = executePythonScriptWithArgs(tempFile, args);
I need to catch some directory within the application. For that I have a small demonstration:
String pkgName = TestClass.class.getPackage().getName();
String relPath = pkgName.replace(".", "/");
URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
File file = new File(resource.getPath());
System.out.println("Dir exists:" + file.exists());
While running application from IDE I receive my goal and I can find my directory. But running application as JAR file, does not return a valid "file" (from Javas perspective) and my sout gives me back File exists:false. Is there some way to get this file? In this case, the file is a directory.
Java ClassPath is an abstraction that differs from a filesystem abstraction.
A classpath element may exist in two physical ways:
exploded with classpath pointing to the root directory
packed in a JAR archive
Unfortunatelly, the file.getPath does return a File object if classpath is pointing to file system but it does not if you refer to a JAR file.
In 99% of all cases you should read the contents of a resource using InputStream.
Here is a snippet, that uses IOUtils from apache commons-io to load the whole file content into a String.
public static String readResource(final String classpathResource) {
try {
final InputStream is = TestClass.class.getResourceAsStream(classpathResource);
// TODO verify is != null
final String content = IOUtils.toString(
is, StandardCharsets.UTF_8);
return content;
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}
I am trying to access a directory inside my jar file. I want to go through every of the files inside the directory itself. I tried using the following:
File[] files = new File("ressources").listFiles();
for (File file : files) {
XMLParser parser = new XMLParser(file.getAbsolutePath());
// some work
}
If I test this, it works well. But once I put the contents into the jar, it doesn't because of several reasons. If I use this code, the URL always points outside the jar.
structure of my project :
src
controllers
models
class that containt traitement
views
ressources
See this:
How do I list the files inside a JAR file?
Basically, you just use a ZipInputStream to find a list of files (a .jar is the same as a .zip)
Once you know the names of the files, you can use getClass().getResource(String path) to get the URL to the file.
I presume this jar is on your classpath.
You can list all the files in a directory using the ClassLoader.
First you can get a list of the file names then you can get URLs from the ClassLoader for individual files:
public static void main(String[] args) throws Exception {
final String base = "/path/to/folder/inside/jar";
final List<URL> urls = new LinkedList<>();
try (final Scanner s = new Scanner(MyClass.class.getResourceAsStream(base))) {
while (s.hasNext()) {
urls.add(MyClass.class.getResource(base + "/" + s.nextLine()));
}
}
System.out.println(urls);
}
You can do whatever you want with the URL - either read and InputStream into memory or copy the InputStream into a File on your hard disc.
Note that this definitely works with the URLClassLoader which is the default, if you are using an applet or a custom ClassLoader then this approach may not work.
NB:
You have a typo - its resources not ressources.
You should use reverse domain name notation for your project, this is the convention.