File.renameTo()
Wygląda na to, że Java jest problematyczna, zwłaszcza w systemie Windows. Jak mówi dokumentacja API ,
Wiele aspektów zachowania tej metody jest z natury zależnych od platformy: operacja zmiany nazwy może nie być w stanie przenieść pliku z jednego systemu plików na inny, może nie być atomowy i może się nie powieść, jeśli plik z docelową abstrakcyjną nazwą ścieżki już istnieje. Zawsze należy sprawdzać zwracaną wartość, aby upewnić się, że operacja zmiany nazwy zakończyła się pomyślnie.
W moim przypadku w ramach procedury aktualizacji muszę przenieść (zmienić nazwę) katalog, który może zawierać gigabajty danych (wiele podkatalogów i plików o różnych rozmiarach). Przenoszenie jest zawsze wykonywane na tej samej partycji / dysku, więc nie ma potrzeby fizycznego przenoszenia wszystkich plików na dysku.
Nie powinno być żadnych blokad plików w zawartości katalogu do przeniesienia, ale wciąż dość często funkcja renameTo () nie wykonuje swojego zadania i zwraca wartość false. (Zgaduję tylko, że być może niektóre blokady plików wygasają nieco arbitralnie w systemie Windows.)
Obecnie mam metodę awaryjną, która wykorzystuje kopiowanie i usuwanie, ale to jest do bani, ponieważ może to zająć dużo czasu, w zależności od rozmiaru folderu. Rozważam również po prostu udokumentowanie faktu, że użytkownik może ręcznie przenieść folder, aby potencjalnie uniknąć czekania godzinami. Ale Właściwa Droga byłaby oczywiście czymś automatycznym i szybkim.
Moje pytanie brzmi więc, czy znasz alternatywne, niezawodne podejście do szybkiego przenoszenia / zmiany nazwy w Javie w systemie Windows , albo za pomocą zwykłego JDK, albo jakiejś zewnętrznej biblioteki. Lub jeśli znasz łatwy sposób na wykrycie i zwolnienie blokad plików dla danego folderu i całej jego zawartości (prawdopodobnie tysięcy pojedynczych plików), to też byłoby w porządku.
Edycja : w tym konkretnym przypadku wydaje się, że uniknęliśmy używania renameTo()
, biorąc pod uwagę kilka innych rzeczy; zobacz tę odpowiedź .
Odpowiedzi:
Zobacz także
Files.move()
metodę w JDK 7.Przykład:
String fileName = "MyFile.txt"; try { Files.move(new File(fileName).toPath(), new File(fileName).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); } catch (IOException ex) { Logger.getLogger(SomeClass.class.getName()).log(Level.SEVERE, null, ex); }
źródło
Warto, kilka dalszych pojęć:
W systemie Windows
renameTo()
wydaje się nie powieść, jeśli katalog docelowy istnieje, nawet jeśli jest pusty. Zaskoczyło mnie to, ponieważ próbowałem na Linuksie, gdzierenameTo()
udało się, jeśli cel istniał, o ile był pusty.(Oczywiście nie powinienem zakładać, że tego rodzaju rzeczy działają tak samo na różnych platformach; dokładnie o tym ostrzega Javadoc).
Jeśli podejrzewasz, że mogą istnieć pewne utrzymujące się blokady plików, poczekaj chwilę, zanim przeniesienie / zmiana nazwy może pomóc. (W jednym miejscu w naszym instalatorze / uaktualniaczu dodaliśmy akcję "uśpienia" i nieokreślony pasek postępu przez około 10 sekund, ponieważ niektóre pliki mogą się zawiesić). Być może nawet wykonaj prosty mechanizm ponawiania, który próbuje
renameTo()
, a następnie czeka przez pewien okres (który może stopniowo się zwiększać), aż operacja się powiedzie lub osiągnięty zostanie limit czasu.W moim przypadku wydaje się, że większość problemów została rozwiązana biorąc pod uwagę oba powyższe, więc w końcu nie będziemy musieli wykonywać natywnego wywołania jądra lub czegoś podobnego.
źródło
Oryginalny post zawierał prośbę o „alternatywne, niezawodne podejście do szybkiego przenoszenia / zmiany nazwy w Javie w systemie Windows, przy użyciu zwykłego JDK lub jakiejś zewnętrznej biblioteki”.
Inną opcją, o której tu jeszcze nie wspomniano, jest wersja 1.3.2 lub nowsza biblioteki apache.commons.io , która zawiera FileUtils.moveFile () .
Zgłasza IOException zamiast zwracać wartość logiczną false w przypadku błędu.
Zobacz także odpowiedź wielkiego lpa w tym innym wątku .
źródło
java.nio.file.Path.moveTo()
W moim przypadku wydawało się, że był to martwy obiekt w mojej własnej aplikacji, która zachowała uchwyt do tego pliku. Więc to rozwiązanie zadziałało dla mnie:
for (int i = 0; i < 20; i++) { if (sourceFile.renameTo(backupFile)) break; System.gc(); Thread.yield(); }
Zaleta: jest dość szybka, ponieważ nie ma funkcji Thread.sleep () z konkretnym zakodowanym czasem.
Wada: ten limit 20 to pewna liczba zakodowana na stałe. We wszystkich moich testach wystarczy i = 1. Ale dla pewności zostawiłem to o 20.
źródło
Wiem, że wydaje się to trochę hakerskie, ale z powodu tego, do czego go potrzebowałem, wydaje się, że buforowani czytelnicy i pisarze nie mają problemu z tworzeniem plików.
void renameFiles(String oldName, String newName) { String sCurrentLine = ""; try { BufferedReader br = new BufferedReader(new FileReader(oldName)); BufferedWriter bw = new BufferedWriter(new FileWriter(newName)); while ((sCurrentLine = br.readLine()) != null) { bw.write(sCurrentLine); bw.newLine(); } br.close(); bw.close(); File org = new File(oldName); org.delete(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
Działa dobrze w przypadku małych plików tekstowych jako część parsera, po prostu upewnij się, że oldName i newName to pełne ścieżki do lokalizacji plików.
Pozdrawiam Kactus
źródło
Poniższy fragment kodu NIE jest „alternatywą”, ale sprawdził się niezawodnie zarówno w środowisku Windows, jak i Linux:
public static void renameFile(String oldName, String newName) throws IOException { File srcFile = new File(oldName); boolean bSucceeded = false; try { File destFile = new File(newName); if (destFile.exists()) { if (!destFile.delete()) { throw new IOException(oldName + " was not successfully renamed to " + newName); } } if (!srcFile.renameTo(destFile)) { throw new IOException(oldName + " was not successfully renamed to " + newName); } else { bSucceeded = true; } } finally { if (bSucceeded) { srcFile.delete(); } } }
źródło
W systemie Windows używam,
Runtime.getRuntime().exec("cmd \\c ")
a następnie używam funkcji zmiany nazwy z wiersza poleceń, aby faktycznie zmienić nazwy plików. Jest znacznie bardziej elastyczny, np. Jeśli chcesz zmienić rozszerzenie wszystkich plików txt w katalogu do bak, po prostu napisz to do strumienia wyjściowego:zmień nazwę * .txt * .bak
Wiem, że to nie jest dobre rozwiązanie, ale najwyraźniej zawsze działało na mnie, znacznie lepiej niż obsługa wbudowanej Java.
źródło
Dlaczego nie....
import com.sun.jna.Native; import com.sun.jna.Library; public class RenamerByJna { /* Requires jna.jar to be in your path */ public interface Kernel32 extends Library { public boolean MoveFileA(String existingFileName, String newFileName); } public static void main(String[] args) { String path = "C:/yourchosenpath/"; String existingFileName = path + "test.txt"; String newFileName = path + "renamed.txt"; Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); kernel32.MoveFileA(existingFileName, newFileName); } }
działa na nwindows 7, nic nie robi, jeśli nie istnieje plik existingFile, ale oczywiście mógłby być lepiej wyposażony, aby to naprawić.
źródło
Miałem podobny problem. Plik został skopiowany raczej w ruchu w systemie Windows, ale działał dobrze w systemie Linux. Rozwiązałem problem, zamykając otwarty plik FileInputStream przed wywołaniem funkcji renameTo (). Przetestowano w systemie Windows XP.
fis = new FileInputStream(originalFile); .. .. .. fis.close();// <<<---- Fixed by adding this originalFile.renameTo(newDesitnationForOriginalFile);
źródło
W moim przypadku błąd był w ścieżce katalogu nadrzędnego. Może to błąd, musiałem użyć podciągu, aby uzyskać poprawną ścieżkę.
try { String n = f.getAbsolutePath(); **n = n.substring(0, n.lastIndexOf("\\"));** File dest = new File(**n**, newName); f.renameTo(dest); } catch (Exception ex) { ...
źródło
Wiem, że to jest do bani, ale alternatywą jest utworzenie skryptu nietoperza, który wyświetla coś prostego, takiego jak „SUKCES” lub „BŁĄD”, wywołanie go, poczekanie na wykonanie i sprawdzenie wyników.
Runtime.getRuntime (). Exec ("cmd / c start test.bat");
Ten wątek może być interesujący. Sprawdź również klasę Process, aby dowiedzieć się, jak odczytywać dane wyjściowe konsoli z innego procesu.
źródło
Możesz spróbować robocopy . Nie jest to dokładnie „zmiana nazwy”, ale jest to bardzo niezawodne.
źródło
Aby przenieść / zmienić nazwę pliku, możesz użyć tej funkcji:
BOOL WINAPI MoveFile( __in LPCTSTR lpExistingFileName, __in LPCTSTR lpNewFileName );
Jest zdefiniowany w kernel32.dll.
źródło
File srcFile = new File(origFilename); File destFile = new File(newFilename); srcFile.renameTo(destFile);
Powyższy kod to prosty kod. Testowałem na Windows 7 i działa doskonale.
źródło