niedziela, lipca 05, 2009

Tworzenie poprawnych nazw plików :-)

Java jest świetna do pisania przenośnych aplikacji, które będą działały na różnych systemach.
Co prawda Swing wcale nie gwarantuje tego, że aplikacja będzie zachowywała się zawsze tak samo ;-), głównie zresztą za przyczyną Apple, które wie lepiej i ma swoją maszynę wirtualną. Ale mimo wszystko jest dobrze......

Do czasu gdy chcesz pobrać plik z sieci i zapisać go na dysku ;-)

W OOo2GD do stworzenia nazwy pliku używam tytułu dokumentu, a w tym tytule użytkownik może złośliwie użyć takich znaków jak :, albo *, albo i / czy \. Ogólnie zgroza ;-)

Dlatego w ramach nanoszenia poprawki na OOo2GD postanowiłem zabezpieczyć je przed problemami z dziwnymi nazwami plików.

Przy pierwszym podejściu wyrzucam z nazwy plików znaki / oraz separator elementów ścieżki do pliku [w Uniksach / w Windows \]... dalej jest już ciekawiej ;-)

public static String findAvailableFileName(String destFileURI) {
String destFileName = destFileURI.substring(0,destFileURI.lastIndexOf("."));
String destFileExt = destFileURI.substring(destFileURI.lastIndexOf(".")+1);
int count = 1;
File f;
while ((f=new File(destFileURI)).exists()) {
destFileURI=destFileName+"("+(count++)+")"+"."+destFileExt;
}
String fName = f.getName();
String fPath = f.getParent();
// Now we need to check if given file name is valid for file system, and if it isn't we need to convert it to valid form
if (!(testIfFileNameIsValid(destFileURI))) {
List forbiddenCharsPatterns = new ArrayList();
forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
for (String pattern:forbiddenCharsPatterns) {
String nameToTest = fName;
nameToTest = nameToTest.replaceAll(pattern, "_");
destFileURI=fPath+"/"+nameToTest;
count=1;
destFileName = destFileURI.substring(0,destFileURI.lastIndexOf("."));
destFileExt = destFileURI.substring(destFileURI.lastIndexOf(".")+1);
while ((f=new File(destFileURI)).exists()) {
destFileURI=destFileName+"("+(count++)+")"+"."+destFileExt;
}
if (testIfFileNameIsValid(destFileURI)) break;
}
}
return destFileURI;
}

private static boolean testIfFileNameIsValid(String destFileURI) {
boolean valid = false;
try {
File candidate = new File(destFileURI);
String canonicalPath = candidate.getCanonicalPath();
boolean b = candidate.createNewFile();
if (b) {
candidate.delete();
}
valid = true;
} catch (IOException ioEx) { }
return valid;
}

Ładniejsze źródła tutaj.

Idea jest taka, po pierwsze próbujemy pozbyć się znaku ":", który jak się wydaje nie jest wspierany przez żaden system plików jako element nazwy pliku :-)
Później próbujemy pozbyć się znaków specyficznych dla FAT32 [jak mi się wydaje], czyli np. nawiasów kwadratowych, czy masek [* i prawdopodobnie również ?, którego w kodzie jeszcze nie ma ;-)].
Gdy to nie poskutkuje idziemy na całość i wyrzucamy wszystkie znaki, które nie są literami łacińskimi lub cyframi.

Do tego dobrze by było by nie nadpisać istniejącego już pliku, dlatego na każdym kroku należy się upewnić, że dana nazwa jest dostępna.

Samo zaś testowanie czy plik o danej nazwie może zostać stworzony to smutny przykład wykorzystania wyjątków. Najpierw próbujemy pobrać kanoniczną ścieżkę do pliku przez getCanonicalPath(), które w przypadku gdy nazwa pliku jest zła wyrzuci wyjątek, a następnie próbujemy stworzyć plik. To drugie jest zbędne, ale w ramach dmuchania na zimne lepiej je zostawić ;-)


Podobne postybeta
Pomnóżmy sobie duże liczby ;-)
Zinwigiluj się sam ;-)
SleepAdvisor - komórka pomaga w wyspaniu się ;-)
Umiejętność programowania pomaga :-)
Niecne wykorzystanie refleksji... czyli jak poszukać tekstu w drzewie obiektów? ;-)