10

How to get the short-filename for a long-filename in Windows using Java?

I need to determine the short-filenames of files, stored on a Windows system, using Java(tm).

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
Osmund Francis
  • 771
  • 10
  • 27
  • I think this thread could be useful: http://stackoverflow.com/questions/1844688/read-all-files-in-a-folder – Ernestas Kardzys Sep 19 '13 at 11:37
  • 2
    This thread is useful as it highlights how to use JNA (Java(tm) Native Access) to access the Windows Win32 API function GetShortPathName() : http://stackoverflow.com/questions/11038595/java-call-for-windows-api-getshortpathname – Osmund Francis Sep 20 '13 at 18:51
  • This thread details using command-line in the same way: http://stackoverflow.com/questions/10227144/convert-long-filename-to-short-filename-using-cmd-exe – Osmund Francis Sep 20 '13 at 19:01

2 Answers2

10

Self Answer

There are related questions with related answers. I post this solution, however, because it uses Java(tm) code without the need for external libraries. Additional solutions for different versions of Java and/or Microsoft(R) Windows(tm) are welcome.

Main Concept

Main concept lies in calling CMD from Java(tm) by means of the runtime class:

cmd /c for %I in ("[long file name]") do @echo %~fsI

Solution

Tested on Java SE 7 running on Windows 7 system (Code has been reduced for brevity).

    public static String getMSDOSName(String fileName)
    throws IOException, InterruptedException {

    String path = getAbsolutePath(fileName);

    // changed "+ fileName.toUpperCase() +" to "path"
    Process process =
        Runtime.getRuntime().exec(
            "cmd /c for %I in (\"" + path + "\") do @echo %~fsI");

    process.waitFor();

    byte[] data = new byte[65536];
    int size = process.getInputStream().read(data);

    if (size <= 0)
        return null;

    return new String(data, 0, size).replaceAll("\\r\\n", "");
}

public static String getAbsolutePath(String fileName)
    throws IOException {
    File file = new File(fileName);
    String path = file.getAbsolutePath();

    if (file.exists() == false)
        file = new File(path);

    path = file.getCanonicalPath();

    if (file.isDirectory() && (path.endsWith(File.separator) == false))
        path += File.separator;

    return path;
}
Osmund Francis
  • 771
  • 10
  • 27
  • 2
    The solution is very good but has a small bug. The fileName.toUpperCase() should remove the toUpperCase since that conversion might do some conversion that is not compatible with how windows charset works. In return windows might not find the file. I had errors with a filename like "20140506_224702_Bäckerstraße.jpg" which through conversion became "20140506_224702_BÄCKERSTRASSE.JPG". – raisercostin Aug 06 '14 at 15:57
-1

I found a slight problem Osmund's solution. It doesn't work properly for this file name for some reason:

N:\directoryY\tmp\temp\asdfasdf sdf dsfasdf [dfadss]\[asdfas] asdfasd asd asdfasdf ~fdfsdfdfdsfdfdfdfdfd~ TTTm7-9  [RR 1234a5678 A.888 OKOK]a

I'm not really sure why exactly. But if you run the command a slightly different way (using ProcessBuilder), it works. Here is the new code (I am using BufferedReader to read the output, it is much cleaner).

    public static String getMSDOSName(String path) throws IOException, InterruptedException {
        Process process = new ProcessBuilder().command("cmd", "/c", "for %I in (\"" + path + "\") do @echo %~fsI").start();
        process.waitFor();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            return br.readLine();
        }
    }

This is the output of the original solution vs my solution. The original solution fails to shorten the last path element:

N:\DIRECT~1\tmp\temp\ASDFAS~1\[asdfas] asdfasd asd asdfasdf ~fdfsdfdfdsfdfdfdfdfd~ TTTm7-9 [RR 1234a5678 A.888 OKOK]a
N:\DIRECT~1\tmp\temp\ASDFAS~1\_ASDFA~1.888