Identify extension of a file based on its stem name - java

I have a simple problem that I am quite struggling with. I have several files in a directory and I am reading them and passing processing them based on their type (extension). However, as an input, I receive a path to the file without extension so I have to identify the type myself.
example (files):
files/file1.txt
files/file1.txt
files/pic1.jpg
----------------
String path = "files/file1";
String ext = FilenameUtils.getExtension(path); // this returns null
Is there a way to identify the type of file when the extension is not included in the path?

Your best bet here is to "do it yourself" by implementing instances of FileTypeDetectors.
When you have this, you can then just use Files.probeContentType() to have a string returned which describes the file contents as a MIME type.
The JDK does provide a default implementation but it relies on file extensions, basically; if you have a PNG image named foo.txt, the default implementation will return text/plain where the file is really an image/png.
Which is of course wrong.
Final note: if all you really have is only part of the file name, then use Files.newDirectoryStream() and provide it with the appropriate DirectoryStream.Filter<Path>. Not sure yet why you only have part of it though.

Since you're only given part of the file name, you'll need to search for files that start with that prefix. Note that there could be multiple matches.
Using java.nio.file
Path prefix = Paths.get(path);
Path directory = prefix.getParent();
try (Stream<Path> stream = Files.list(directory)) {
stream.filter(p -> p.getFileName().startsWith(prefix.getFileName() + "."))
.forEach(p -> System.out.printf("Found %s%n", p));
}
Using java.io
File prefix = new File(path);
File directory = prefix.getParentFile();
List<File> matches = directory.listFiles((dir, name) ->
name.startsWith(prefix.getName() + "."));
for (File match: matches) {
System.out.printf("Found %s%n", match);
}

Files.probeContentType(Path) implements a basic MIME type inquiry you can use (or extend), the internal details of which are platform specific. You can also make a little utility method that walks a Set of extensions. A combination of the two approaches may be necessary, depending on your application.
The MIME type checker will give different results on different releases implementations of the JRE. So, always have a fail-over solution.
See: http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#probeContentType%28java.nio.file.Path
[EDIT]
This actually does not answer the question posited, as this method needs a full, legal Path object to work on. If you are given just the stem name, and the extension is missing, then you neither have an extension to work with nor a valid Path name for Files to work with [and probeContentType() may, in some implementations, just use the extension anyway.]
I'm not sure how you can do this without Path that refers to a real on-disk file that the JRE can access, or by hand if you don't have an extension. If you don't have a File of some sort, you can't even open it up yourself to attempt file type "magic".

Related

Read a file from resources folder JAVA

I want to get a file from the resource file, and to use it in string.
I tried this :
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("/resources/fileC.p12").getFile());
String data = String.valueOf(file);
but doesnt work, thanks for helping
I tried this but i had a error, it returns a null value
new File In java, File means File. As in, an actual file on your actual harddisk. Resources aren't - they are entries in a jarfile and therefore not a file. Simply put, resources cannot be read this way.
Fortunately, File in general is barking up the wrong tree: The correct abstraction is InputStream or similar - that represents 'any stream of bytes'. A file can be an InputStream. So can a network socket, a blob from a network, or, indeed, a resource being streamed to you by the classloader, which could very well be getting it from a network or generating it whole cloth - classloaders can do that. It's an abstract mechanism.
You're also doing it wrong - you want Type.class.getResource. Your way is needlessly wordy and will fail in exotic scenarios (such as bootloaders and agents and the like, which have no classloader).
class Example {
public String getDataFromFileC() throws IOException {
try (var in = Example.class.getResourceAsStream("/resources/fileC.p12")) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
This:
Uses getResourceAsStream which gives you an inputstream. As I mentioned, if you mention File, you lose. Hence, we don't.
Uses the proper form: MyType.class.get. This avoids issues when subclassing or in root classloader situations.
MyType.class.get needs a leading slash. the getResource on classloaders requires you not to have it (which explains why your snippet wouldn't work in any scenario - that leading slash).
Uses try-with-resources as you should.
Propagates exceptions as you should.
Configures charset which you should do anytime you go from bytes to strings or vice versa.
NB: Depending on your build system, it may package those resources in the jar as /fileC.p12 and not as /resources/fileC.p12 - in fact, that is likely. You may want to update this to "/fileC.p12".
NB2: String.valueOf(file); does not read files. It just calls toString() on the file object which gives you a path. Resources don't have to be a path so this cannot work. They do have a URL, which may or may not be useful. If you want that: return MyClass.class.getResource("/resources/fileC.p12").toString();.
String data = new String(getClass().getResourceAsStream("/fileC.p12").readAllBytes());
Your resource (which shouldn't be seen as a file as it could and probably should be packaged with your app) is at the root, so useful to start with '/' then it can be addressed from any package. Be cautious with Java >= 17 as that will be decoded by default as UTF-8, so if that's not the encoding, you will have to specify what is in the String ctor. It might be safer to do that anyway.

Converting a Jar-URI into a nio.Path

I'm having trouble coverting from a URI to a nio.Path in the general case. Given a URI with multiple schemas, I wish to create a single nio.Path instance to reflect this URI.
//setup
String jarEmbeddedFilePathString = "jar:file:/C:/Program%20Files%20(x86)/OurSoftware/OurJar_x86_1.0.68.220.jar!/com/our_company/javaFXViewCode.fxml";
URI uri = URI.create(jarEmbeddedFilePathString);
//act
Path nioPath = Paths.get(uri);
//assert --any of these are acceptable
assertThat(nioPath).isEqualTo("C:/Program Files (x86)/OurSoftware/OurJar_x86_1.0.68.220.jar/com/our_company/javaFXViewCode.fxml");
//--or assertThat(nioPath).isEqualTo("/com/our_company/javaFXViewCode.fxml");
//--or assertThat(nioPath).isEqualTo("OurJar_x86_1.0.68.220.jar!/com/our_company/javaFXViewCode.fxml")
//or pretty well any other interpretation of jar'd-uri-to-path any reasonable person would have.
This code currently throws FileSystemNotFoundException on the Paths.get() call.
The actual reason for this conversion is to ask the resulting path about things regarding its package location and file name --so in other words, as long as the resulting path object preserves the ...com/our_company/javaFXViewCode.fxml portion, then its still very convenient for us to use the NIO Path object.
Most of this information is actually used for debugging, so it would not be impossible for me to retrofit our code to avoid use of Paths in this particular instance and instead use URI's or simply strings, but that would involve a bunch of retooling for methods already conveniently provided by the nio.Path object.
I've started digging into the file system provider API and have been confronted with more complexity than I wish to deal with for such a small thing. Is there a simple way to convert from a class-loader provided URI to a path object corresponding to OS-understandable traversal in the case of the URI pointing to a non-jar file, and not-OS-understandable-but-still-useful traversal in the case where the path would point to a resource inside a jar (or for that matter a zip or tarball)?
Thanks for any help
A Java Path belongs to a FileSystem. A file system is implemented by a FileSystemProvider.
Java comes with two file system providers: One for the operating system (e.g. WindowsFileSystemProvider), and one for zip files (ZipFileSystemProvider). These are internal and should not be accessed directly.
To get a Path to a file inside a Jar file, you need to get (create) a FileSystem for the content of the Jar file. You can then get a Path to a file in that file system.
First, you'll need to parse the Jar URL, which is best done using the JarURLConnection:
URL jarEntryURL = new URL("jar:file:/C:/Program%20Files%20(x86)/OurSoftware/OurJar_x86_1.0.68.220.jar!/com/our_company/javaFXViewCode.fxml");
JarURLConnection jarEntryConn = (JarURLConnection) jarEntryURL.openConnection();
URL jarFileURL = jarEntryConn.getJarFileURL(); // file:/C:/Program%20Files%20(x86)/OurSoftware/OurJar_x86_1.0.68.220.jar
String entryName = jarEntryConn.getEntryName(); // com/our_company/javaFXViewCode.fxml
Once you have those, you can create a FileSystem and get a Path to the jar'd file. Remember that FileSystem is an open resource and needs to be closed when you are done with it:
try (FileSystem jarFileSystem = FileSystems.newFileSystem(jarPath, null)) {
Path entryPath = jarFileSystem.getPath(entryName);
System.out.println("entryPath: " + entryPath); // com/our_company/javaFXViewCode.fxml
System.out.println("parent: " + entryPath.getParent()); // com/our_company
}

both File.isFile() and File.isDirectory() is returning false

I have a file with name "aaaäaa.xls"
For this, File.isFile() and File.isDirectory() is returning false? why it is returning false in Linux?
Please try the following code example
if(!pFile.exists()){
throw new FileNotFoundException();
}
boolean isDir = pFile.isDirectory();
boolean isFile = pfile.isFile();
the file is not a file
if it is not a directory and, in addition, satisfies other system-dependent criteria
if the exception is thrown, you have to check the file path.
According to the documentation:
public boolean isFile()
Returns:
true if and only if the file denoted by this abstract pathname exists and is
a normal file; false otherwise.
From this basis, your file either doesn't exist or is not a normal file.
Possible reasons of the 1st:
file doesn't exist;
file can't be accessed;
file name is mistyped;
the character encoding used in your program isn't the
same as that used when you created the file.
Possible reasons of the 2nd:
it's not a regular file.
Or it's a bug in JVM. It's also possible though unlikely. For example at once I had problems with an exclamation mark in path names - Bug 4523159.
If you want to access the file in any way, consider calling dir.listFiles() and work with its return value.
(answer is partially based on this thread)
Check the permissions of the parent directories of this file. Some of these directories may not have execute permission for the current user.
The execute bit of directory allows the affected user to enter it and access files and directories inside
I know that this question was asked five years ago, in fact, I got to it because I had the same problem, I am creating a List of all the files in a given path, like this:
File files = Paths.get(path).toFile();
List<String> filenames = Arrays.asList(files.list());
The thing is, that path contains a directory which is called testing_testing, which is being returned as part of the list.
Then when I do the following test:
for (String filename : filenames) {
if (Files.isDirectory(Paths.get(filename))) {
System.out.println(filename + " is a directory.");
} else {
if(filename.equals("testing_testing")) {
System.out.println("Is this a directory?: " + Files.isDirectory(Paths.get(filename)));
System.out.println("Does the file exists?: " + Files.exists(Paths.get(filename)));
System.out.println("Is this a regular file?: " + Files.isRegularFile(Paths.get(filename)));
System.out.println("Is this a symbolic link?: " + Files.isSymbolicLink(Paths.get(filename)));
}
}
}
It is returning false for Files.isDirectory() and for Files.exists().
Tinkering around for a bit, I noticed that I was only getting the filenames, without the full path to them, which meant that I was only passing testing_testing to Paths.get() instead of passing the full path to get to it, that's why it didn't exists and returned false for both scenarios.
I changed the code to:
Paths.get("C:\test", filename);
And now the tests return the proper values. I don't know if you've already figured this out, because it's been five years since you asked. But for anyone with the same problem, make sure that you're passing the correct path to your files, and then try the other things suggested in previous answers on this same question.
I've also had problems with file.isFile() returning false on files, presumably because the file is not "regular", as noted in other responses to this question. As a workaround, I use file.listFiles() != null, which seems to provide the functionality I need. According to the Java File API:
If this abstract pathname does not denote a directory, then this method returns null. Otherwise an array of File objects is returned.
I got same error when i was testing isFile() on a .txt file.
The problem was the file i created had something.txt with .txt on the name.
Then i renamed something.txt to something
I was really mad with myself
The character encoding used by Java in your case is different from the character encoding in the source file, so the symbol "ä" in the file name cannot be properly decoded by Java, resulting in a different file name. That's why Java cannot find the file. Therefore, the file manipulation functions over this file return "False".
As the safest way to work properly in different build environments, to avoid setting Java character encoding option and also make handling source files easier, use US-ASCII only (7-bit) characters in the source code. As for the other characters, use their Unicode numbers, e.g., instead of "ä" use "\u00e4". So, your filename would become "aaa\u00e4aa.xls".
I've had this issue several times and if everything has been tried then it might be that you have issues with pathing. Any space character is replaced by %20, and it results in an issue.
Therefore, whereas this doesn't work:
File file = new File(Objects.requireNonNull(getClass().getResource(/path/to/file).getPath());
This actually does:
File file = new File(Objects.requireNonNull(getClass().getResource(/path/to/file).getPath().replace("%20", " "));

Java isFile(), isDirectory() without checking for existence

I want to check if a given String is a file or a directory, i've tried the methods isFile() and isDirectory() of the File class but the problem is that if the directory or file doesn't exist these methods returns false, because as stated in the javadoc :
isFile() :
true if and only if the file denoted by this abstract pathname exists
and is a normal file; false otherwise
isDirectory() :
true if and only if the file denoted by this abstract pathname exists
and is a directory; false otherwise
Basically i need two methods without the exist clause ...
So i want to test if the given string complies to a directory format or complies to a file format, in a multiplatform context (so, should work on Windows, Linux and Mac Os X).
Does exist some library that provide these methods ? What could be the best implementation of these methods ?
UPDATE
In the case of a string that could be both(without extension) by default should be identified as directory, if a file with that path does not exist.
So i want to test if the given string complies to a directory format or complies to a file format, in a multiplatform context (so, should work on Windows, Linux and Mac Os X).
In Windows, a directory can have an extension and a file is not required to have an extension. So, you can't tell just by looking at the string.
If you enforce a rule that a directory doesn't have an extension, and a file always has an extension, then you can determine the difference between a directory and a file by looking for an extension.
Why not just wrap them in a call to File#exists()?
File file = new File(...);
if (file.exists()) {
// call isFile() or isDirectory()
}
By doing that, you've effectively negated the "exists" portion of isFile() and isDirectory(), since you're guaranteed that it does exist.
It's also possible that I've misunderstood what you're asking here. Given the second part of your question, are you trying to use isFile() and isDirectory() on non-existent files to see if they look like they're files or directories?
If so, that's going to be tough to do with the File API (and tough to do in general). If /foo/bar/baz doesn't exist, it's not possible to determine whether it's a file or a directory. It could be either.
Sounds like you know what you want, according to your update: if the path doesn't exist and the path has an extension it's a file, if it doesn't it's a directory. Something like this would suffice:
private boolean isPathDirectory(String myPath) {
File test = new File(myPath);
// check if the file/directory is already there
if (!test.exists()) {
// see if the file portion it doesn't have an extension
return test.getName().lastIndexOf('.') == -1;
} else {
// see if the path that's already in place is a file or directory
return test.isDirectory();
}
}
There are rules for what is invalid in a file and/or folder name. For example, Windows doesn't allow *, ?, and a few other characters. Based on what's invalid, you could build a regex expression or some other process/checking system to see if it looks like a file or folder.
This could get complex as you want it to work for many different OS's. Also, as previously posted, there would be no way to tell a file from a folder unless you artificially enforced a convention. For example, directories must end in a front-slash / in Windows.
Having the IF EXISTS check first would help. If IF EXISTS = true, then running the existing File.isDirectory() or File.isFile() code would simplify a lot of this. You would only have to write code for when IF EXISTS = false.

How do I rename (not move) a file in Java 7?

I'm a bit confused with all these new File I/O classes in JDK7.
Let's say, I have a Path and want to rename the file it represents. How do I specify the new name, when again a Path is expected?
Path p = /* path to /home/me/file123 */;
Path name = p.getName(); /* gives me file123 */
name.moveTo(/* what now? */); /* how to rename file123 to file456? */
NOTE: Why do I need JDK7? Handling of symbolic links!
Problem is: I have to do it with files whose names and locations are known at runtime. So, what I need, is a safe method (without exceptional side-effects) to create a new name-Path of some old name-Path.
Path newName(Path oldName, String newNameString){
/* magic */
}
In JDK7, Files.move() provides a short and concise syntax for renaming files:
Path newName(Path oldName, String newNameString) {
return Files.move(oldName, oldName.resolveSibling(newNameString));
}
First we're getting the Path to the new file name using Path.resolveSibling()
and the we use Files.move() to do the actual renaming.
You have a path string and you need to create a Path instance. You can do this with the getPath method or resolve. Here's one way:
Path dir = oldFile.getParent();
Path fn = oldFile.getFileSystem().getPath(newNameString);
Path target = (dir == null) ? fn : dir.resolve(fn);
oldFile.moveTo(target);
Note that it checks if parent is null (looks like your solution don't do that).
OK, after trying everything out, it seems I found the right method:
// my helper method
Path newName(Path oldFile, String newNameString){
// the magic is done by Path.resolve(...)
return oldFile.getParent().resolve(newNameString);
}
// so, renaming is done by:
oldPath.moveTo(newName(oldFile, "newName"));
If you take a look at Apache Commons IO there's a class called FileNameUtils. This does a ton of stuff wrt. file path names and will (amongst other things) reliably split up path names etc. I think that should get you a long way towards what you want.
If the destination path is identical to the source path except for the name of the file, it will be renamed rather than moved.
So for your example, the moveto path should be
/home/me/file456
If you can't get Java to do what you want with Unix I recommend Python scripts (run by your Java program). Python has get support for Unix scripting and it's not Perl :) This might sound inelegant to you but really in a larger program you'll benefit from using the right tool for the job.

Categories

Resources