【问题标题】:Java File exists Case sensitive .jpg and .JPGJava 文件存在区分大小写的 .jpg 和 .JPG
【发布时间】:2016-01-05 02:57:44
【问题描述】:

我使用这个函数来检测我的文件是否存在。虽然我有一些图像存储为 .jpg、.JPG、.png 和 .PNG。但即使真实文件的扩展名为 .JPG 或 .PNG,它也总是将 .jpg 或 .png 返回为真。

在我将其呈现到我的网页后,它会引发错误“加载资源失败:服务器响应状态为 404(未找到)”。

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 "";
}

【问题讨论】:

  • 不,那个问题是问如何通过 abc.txt 加载 Abc.txt。请问如何检测abc.txt或abc.TXT是否存在。
  • 每个员工都有一张图片,但他们存储在不同的扩展名 jpg JPG png 或 PNG 中。但我的函数总是返回 jpg 或 png 而存在的扩展名是 JPG 或 PNG
  • @John Hascal 仍然返回小写扩展
  • @Lucien Stals 我在 Window 上开发,但在 Linux 上托管。它们的工作方式相同。
  • @Jonathan Thoms Common.PHOTO_PATH 是目录路径,例如 /img/myfolder。 Sessions.getCurrent().getWebApp().getRealPath(path) 是 ZK 方法,它将返回“D:/..../img/myfolder”。

标签: java


【解决方案1】:

因此,您希望获取存储在文件系统中的文件的真实大小写敏感名称。让成像我们有以下路径:

  • Linux: 使用 ext4(区分大小写)/testFolder/test.PnG
  • Windows 上使用 NTFS区分大小写)c:\testFolder\test.PnG

现在让我们为每个图像文件创建一些 Java 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

您的问题是File 的所有调用(如File.exists)都被重定向到java.io.FileSystem 类,该类表示由JVM 对文件系统的实际操作系统调用。因此,您无法在 Windows 机器上区分 test.PNGtest.png。 Windows 本身也没有。

但是即使在 Windows 上,每个文件在文件系统中都有一个已定义的名称,例如:test.PnG。如果您键入 dir c:\testFolder,您将在 Windows Explorer 或命令行中看到此内容。

因此,您可以在 Java 中做的是使用 parent directory 上的 File.list 方法,这会导致操作系统列表调用此目录中的所有文件及其真实名称。

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

或者如果您更喜欢File 对象

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

您可以使用它来编写自己的exists 方法,该方法在所有操作系统上都区分大小写

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

像这样使用它:

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



编辑:我不得不承认我错了。有一种方法可以获取文件的真实名称。我总是忽略了File.getCanonicalPath的方法。
还是我们的例子:我们有那个文件 c:\testFolder\test.PnG

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

有了这些知识,您可以为区分大小写的扩展编写一个简单的测试方法,而无需迭代所有文件。

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;
}

像这样使用它:

File f = new File("c://testFolder//test.png");    
checkExtensionCaseSensitive(f, "png"); // false
checkExtensionCaseSensitive(f, "PNG"); // false
checkExtensionCaseSensitive(f, "PnG"); // true

【讨论】:

  • File.getCanonicalPath() 至少在我的设置中不是获取真实文件名的可靠方法。我写了一个小类,它反复将File.getCanonicalPath() 的结果打印到Eclipse 控制台。在程序运行时,将文件“abc.txt”重命名为“Abc.txt”会导致getCanonicalPath() 仍然返回“abc.txt”,直到我重新启动程序/JVM。我的设置是 JDK 6u45 和 Windows 10。我必须使用基于表达式 ArrayUtils.contains( parentFile.list(), realFileName ) 的解决方案。
  • 这在我的 Linux Mint 上的 UnitTest 中不起作用,我的建议是在您的目标系统上对其进行测试,并使用另一个/更好的备用版本。
【解决方案2】:

如果您正在寻找在任何平台上都可以确定文件存在并且区分大小写的函数;这应该这样做:

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;
    }
}

【讨论】:

  • 请注意,getCanonicalPath() 解析符号链接,这可能不是您想要的。
【解决方案3】:

我开始对此有点困惑,因为我之前没有使用过 Apache 的 IOFileFilter,并认为我会添加这个解决方案作为一个尝试的机会。

代码如下:

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);
    }
}

这里有一些测试用例:

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());
    }
}

希望对您有所帮助。

【讨论】:

    【解决方案4】:

    返回文件对象而不是返回 t(文件扩展名)。这样您就可以确定您拥有正确的文件。如果您不想返回文件对象,请返回带有扩展名的文件名。

    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;
    }
    

    【讨论】:

      【解决方案5】:

      按照 NiMa Thr 所说,您可以使用此代码执行您想要的操作:

      在 Windows 上,如果文件存在,无论如何,它都会返回 true。如果文件不存在,则规范名称相同,因此返回 false。

      在 Linux 上,如果文件以不同的大小写存在,规范名称将返回此不同的名称,方法将返回 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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-15
        • 2017-06-07
        • 1970-01-01
        • 1970-01-01
        • 2016-05-27
        • 1970-01-01
        • 2016-10-09
        • 1970-01-01
        相关资源
        最近更新 更多