24

I use this function to detect if my file exists or not. While I have some image stored as .jpg, .JPG, .png, and .PNG. But it always return .jpg or .png as true even if the real file has extension .JPG or .PNG.

After I render it to my webpage it throws an error "Failed to load resource: the server responded with a status of 404 (Not Found)".

public static String getPhotoFileExtension(int empKey){
    try{
        String[] types = {".jpg",".JPG",".png", ".PNG"};
        for(String t : types)
        {
            String path = "/"+Common.PHOTO_PATH + empKey + t;
            File f = new File(Sessions.getCurrent().getWebApp()
                    .getRealPath(path));
            if(f.isFile()) 
                return t;
        }
    }catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}
7
  • 1
    No, that question ask how to load Abc.txt by abc.txt. I ask about how to detect if abc.txt or abc.TXT exists. Commented Jan 5, 2016 at 3:06
  • Each employee has an image, but they store in different extension jpg JPG png or PNG. but my function always return jpg or png while the exists extension is JPG or PNG Commented Jan 5, 2016 at 3:11
  • @John Hascal still return lowercase extension Commented Jan 5, 2016 at 3:39
  • @Lucien Stals I'm developing on Window, but hosting on Linux. They are working the same. Commented Jan 11, 2016 at 3:18
  • @Jonathan Thoms Common.PHOTO_PATH is the directory path e.g /img/myfolder. Sessions.getCurrent().getWebApp().getRealPath(path) is ZK method it will return "D:/..../img/myfolder". Commented Jan 11, 2016 at 9:42

5 Answers 5

22
+50

So you want to get the real case sensitive names of files stored in your filesystem. Lets imaging we have the following paths:

  • on Linux: using ext4 (which is case sensitive) /testFolder/test.PnG
  • on Windows using NTFS (which is not case sensitive) c:\testFolder\test.PnG

Now lets create some Java File Objects to each Image File.

// on Linux
File f1 = new File("/testFolder/test.png");
File f2 = new File("/testFolder/test.PNG");
File f3 = new File("/testFolder/test.PnG");
f1.exists(); // false
f2.exists(); // false
f3.exists(); // true

// on Windows
File f1 = new File("c:\\testFolder\\test.png");
File f2 = new File("c:\\testFolder\\test.PNG");
File f3 = new File("c:\\testFolder\\test.PnG");
f1.exists(); // true
f2.exists(); // true
f3.exists(); // true

Your problem is that all calls of File like File.exists are redirected to the java.io.FileSystem class that represents real Operating System calls of your File System by the JVM. So you cannot distinguish on Windows Machines between test.PNG and test.png. Neither do Windows itself.

But even on Windows each File has a defined name in the File System that could be for example: test.PnG. You will see this in your Windows Explorer or in Command Line if you type dir c:\testFolder.

So what you can do in Java is use the File.list method on the parent directory that results in the Operating System list call for all files in this directory with their real names.

File dir = new File("c://testFolder//");
for(String fileName : dir.list())
    System.out.println(fileName);
// OUTPUT: test.PnG

or if you prefer File Objects

File dir = new File("c://testFolder//");
for(File file : dir.listFiles())
    System.out.println(file.getName());
// OUTPUT: test.PnG

You can use this to write your own exists Method that is case sensitive on all operating systems

public boolean exists(File dir, String filename){
    String[] files = dir.list();
    for(String file : files)
        if(file.equals(filename))
            return true;
    return false;
}

Use it like this:

File dir = new File("c:\\testFolder\\");
exists(dir, "test.png");   // false
exists(dir, "test.PNG");   // false
exists(dir, "test.PnG");   // true



EDIT: I have to admit that I was wrong. There is a way to get the real name of a File. I always overlooked the method File.getCanonicalPath.
Again our example: We have that File c:\testFolder\test.PnG.

File f = new File("c://testFolder//test.png");
System.out.println(f.getCanonicalPath());
// OUTPUT: C:\testFolder\test.PnG

With that knowledge you can write a simple test method for the case sensitive extension without iterating all files.

public boolean checkExtensionCaseSensitive(File _file, String _extension) throws IOException{
    String canonicalPath = _file.getCanonicalPath();
    String extension = "";
    int i = canonicalPath.lastIndexOf('.');
    if (i > 0) {
        extension = canonicalPath.substring(i+1);
        if(extension.equals(_extension))
            return true;
    }
    return false;
}

Use it like this:

File f = new File("c://testFolder//test.png");    
checkExtensionCaseSensitive(f, "png"); // false
checkExtensionCaseSensitive(f, "PNG"); // false
checkExtensionCaseSensitive(f, "PnG"); // true
Sign up to request clarification or add additional context in comments.

2 Comments

File.getCanonicalPath() is, at least with my setup, not a reliable way to get the real filename. I wrote a small class which repeatedly prints the result of File.getCanonicalPath() to the Eclipse console. While the program runs, renaming the file "abc.txt" to "Abc.txt" results in getCanonicalPath() still returning "abc.txt", until I restart the program / JVM. My setup is JDK 6u45 and Windows 10. I had to use a solution based on the expression ArrayUtils.contains( parentFile.list(), realFileName ).
This does not work in a UnitTest on my Linux Mint, my suggestion is to test it on your targeted system and have another / a better fallback version.
5

If you are looking for a function that in any platform can determine existence of a file and is case-sensitive; this should do it :

public static boolean fileExistsCaseSensitive(String path) {
    try {
        File file = new File(path);
        return file.exists() && file.getCanonicalFile().getName().equals(file.getName());
    } catch (IOException e) {
        return false;
    }
}

1 Comment

Be careful though, getCanonicalPath() resolves symlinks, which may not be what you want.
3

I started messing around a little with this because I haven't used Apache's IOFileFilter before and thought that I would add this solution as a chance to play with it a little.

Here is the code:

import java.io.File;
import java.util.Collection;
import java.util.Optional;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;

public class CaseInsensitiveFileFinder {

    /**
     * Attempts to find a file with the given <code>fileName</code> (irrespective of case) in the given
     * <code>absoluteDirPath</code>. Note that while this method is able to find <code>fileName</code> ignoring case, it
     * may not be able to do so if <code>absoluteDirPath</code> is in an incorrect case - that behavior is OS dependent.
     * 
     * @param absoluteDirPath the absolute path of the parent directory of <code>fileName</code> (e.g. "/Users/me/foo")
     * @param fileName the name of the file including extension that may or may not be the correct case
     * (e.g. myfile.txt)
     * @return an optional reference to the file if found, {@link Optional#empty()} will be returned if the file is not
     * found
     */
    public Optional<File> findFileIgnoreCase(String absoluteDirPath, final String fileName) {

        File directory = new File(absoluteDirPath);
        if (!directory.isDirectory()) {
            throw new IllegalArgumentException("Directory '" + absoluteDirPath + "' isn't a directory.");
        }
        IOFileFilter caseInsensitiveFileNameFilter = new IOFileFilter() {
            @Override
            public boolean accept(File dir, String name) {
                boolean isSameFile = fileName.equalsIgnoreCase(name);
                return isSameFile;
            }

            @Override
            public boolean accept(File file) {
                String name = file.getName();
                boolean isSameFile = fileName.equalsIgnoreCase(name);
                return isSameFile;
            }
        };
        Collection<File> foundFiles = FileUtils.listFiles(directory, caseInsensitiveFileNameFilter, null);
        if (foundFiles == null || foundFiles.isEmpty()) {
            return Optional.empty();
        }
        if (foundFiles.size() > 1) {
            throw new IllegalStateException(
                    "More requirements needed to determine what to do with more than one file. Pick the closest match maybe?");
        }
        // else exactly one file
        File foundFile = foundFiles.iterator().next();
        return Optional.of(foundFile);
    }
}

And here are some test cases:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.util.Optional;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import com.google.common.io.Files;

/**
 * Non-quite-unit tests for {@link CaseInsensitiveFileFinder} class.
 */
public class CaseInsensitiveFileFinderTest {

    private static String APPENDABLE_NEW_TMP_DIR_PATH;

    /**
     * Create the files with different cases.
     * @throws IOException 
     */
    @BeforeClass
    public static void setup() throws IOException {
        File newTmpDir = Files.createTempDir();
        String newTmpDirPath = newTmpDir.getCanonicalPath();
        final String appendableNewTmpDirPath;
        String fileSeparator = System.getProperty("file.separator");
        if (!newTmpDirPath.endsWith(fileSeparator)) {
            appendableNewTmpDirPath = newTmpDirPath + fileSeparator;
        }
        else {
            appendableNewTmpDirPath = newTmpDirPath;
        }
        CaseInsensitiveFileFinderTest.APPENDABLE_NEW_TMP_DIR_PATH = appendableNewTmpDirPath;

        File foofileDotPng = new File(appendableNewTmpDirPath + "FOOFILE.PNG");
        Files.touch(foofileDotPng);
        assertTrue(foofileDotPng.isFile());
        File barfileDotJpg = new File(appendableNewTmpDirPath + "BARFILE.JPG");
        Files.touch(barfileDotJpg);
        assertTrue(barfileDotJpg.isFile());
    }

    @AfterClass
    public static void teardown() throws IOException {
        File newTmpDir = new File(CaseInsensitiveFileFinderTest.APPENDABLE_NEW_TMP_DIR_PATH);
        assertTrue(newTmpDir.isDirectory());
        // delete even though directory isn't empty
        FileUtils.deleteDirectory(newTmpDir);
    }

    @Test
    public void findFooFilePngUsingLowercase() throws IOException {
        CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
        Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(APPENDABLE_NEW_TMP_DIR_PATH, "foofile.png");
        assertTrue(optFoundFile.isPresent());
        File foundFile = optFoundFile.get();
        assertTrue(foundFile.isFile());
        assertEquals(APPENDABLE_NEW_TMP_DIR_PATH + "FOOFILE.PNG", foundFile.getCanonicalPath());
    }

    @Test
    public void findBarFileJpgUsingLowercase() throws IOException {
        CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
        Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(APPENDABLE_NEW_TMP_DIR_PATH, "barfile.jpg");
        assertTrue(optFoundFile.isPresent());
        File foundFile = optFoundFile.get();
        assertTrue(foundFile.isFile());
        assertEquals(APPENDABLE_NEW_TMP_DIR_PATH + "BARFILE.JPG", foundFile.getCanonicalPath());
    }

    @Test
    public void findFileThatDoesNotExist() {
        CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
        Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(APPENDABLE_NEW_TMP_DIR_PATH, "dne.txt");
        assertFalse(optFoundFile.isPresent());
    }

    @Test
    public void findFooFileUsingDirWithNoTrailingFileSeparator() throws IOException {
        CaseInsensitiveFileFinder fileFinder = new CaseInsensitiveFileFinder();
        String newDirPathWithNoTrailingFileSep = StringUtils.chop(APPENDABLE_NEW_TMP_DIR_PATH);
        Optional<File> optFoundFile = fileFinder.findFileIgnoreCase(newDirPathWithNoTrailingFileSep, "FOOFILE.PNG");
        assertTrue(optFoundFile.isPresent());
        File foundFile = optFoundFile.get();
        assertTrue(foundFile.isFile());
        assertEquals(APPENDABLE_NEW_TMP_DIR_PATH + "FOOFILE.PNG", foundFile.getCanonicalPath());
    }
}

Hope that helps.

Comments

1

Instead of returning t (the file extension) return the file Object. That way your certain that you have the correct file. If you don't want to return the file object return the file name with the extension.

public static File getPhotoFileExtension(int empKey){
    try{
        String[] types = {".jpg",".JPG",".png", ".PNG"};
        for(String t : types)
        {
            String path = "/"+Common.PHOTO_PATH + empKey + t;
            File f = new File(Sessions.getCurrent().getWebApp()
                    .getRealPath(path));
            if(f.isFile()) 
                return f;
        }
    }catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Comments

0

With that NiMa Thr said, you can do what you are looking for with this code :

On Windows, if the file exists, with any case, it will return true. If the file doesn't exists, the canonical name will be the same, so it will return false.

On Linux, if the file exists with a different case, the canonical name will return this different name, and the method will return true.

public static boolean fileExistsCaseInsensitive(String path) {
    try {
        File file = new File(path);
        return file.exists() || !file.getCanonicalFile().getName().equals(file.getName());
    } catch (IOException e) {
        return false;
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.