2015-04-28 11 views
6

Windows'ta numaralı Java'yı doğru bir şekilde kullanarak "geçici dosya yaz ve yeniden adlandır" işlevini uygulamamaya çalışıyorum. Dosyaların yeniden adlandırılmasının "atomik işlem" olduğunu (aslında "atomik" ifadesiyle gerçekte olduğu anlamına gelir) önerir. https://stackoverflow.com/a/20570968/65458, tmp dosyasının yazılmasını ve yeniden adlandırmanın çapraz platform olduğunu ve son dosyanın bulunmadığını ya da diğer işlem tarafından işlenebilmesini sağlar. Bu yüzden aslında bu yaklaşımı uygulamaya çalıştım. Aşağıda benim girişimlerimin özeti. Asıl soru için - dibe atlayın.Windows'ta Java'da nasıl atomik olarak yeniden adlandırma dosyası oluşturulur?

java.nio.file.Files Kullanılması:

yazma yöntemleri

Ben yazının çeşitli yollar ve yeniden adlandırma dosya ( content ve charset sırasıyla String ve Charset vardır) denedik

Files.copy(new ByteArrayInputStream(content.getBytes(charset)), tmpFile); 
Files.move(tmpFile, finalFile, StandardCopyOption.ATOMIC_MOVE); 

Guava (14) kullanma ve java.io.File:

com.google.common.io.Files.write(content, tmpFile, charset); 
tmpFile.renameTo(finalFile); 

Hatta daha karanlık yaklaşımlar:

try (OutputStream os = new FileOutputStream(tmpFile); 
     Writer writer = new OutputStreamWriter(os, charset)) { 
    writer.write(content); 
} 
Runtime.getRuntime().exec(
     new String[] { "cmd.exe", "/C", "move " + tmpFile + " " + finalFile }).waitFor(); 

okuma yöntemleri Şimdi

(bunun başka bir süreç olabilir gerçek hayatta, testlerde olduğum için iplik) Başka bir iş parçacığı varsayalım

birini yürütülürken

ortak fonksiyonu ile:

void waitUntilExists() throws InterruptedException { 
    while (!java.nio.file.Files.exists(finalFile)) { 
     NANOSECONDS.sleep(1); 
    } 
} 

kullanma kod sürümleri aşağıdaki:

waitUntilExists(); 
return new String(Files.readAllBytes(finalFile), charset); 

Guava (14) kullanarak:

waitUntilExists(); 
return new String(com.google.common.io.Files.toByteArray(finalFile.toFile()), charset); 

Hatta daha karanlık yaklaşımlar:

waitUntilExists(); 
StringBuilder sb = new StringBuilder(); 
try (InputStream is = new FileInputStream(finalFile.toFile())) { 
    byte[] buf = new byte[8192]; 
    int n; 
    while ((n = is.read(buf)) > 0) { 
     sb.append(new String(buf, 0, n, charset)); 
    } 
} 
return sb.toString(); 

Sonuçlar

kullanıyorum kullanarak okumak "Eğer java.nio.file.Files yaklaşım ", her şey iyi çalışıyor.

Bu kodu Linux'ta çalıştırırsam (bu sorunun kapsamı dışında), her şey yolunda gidiyor. ı uygulamak durumunda

Ancak % 0.5 üstünde bir olasılıkla (0.005) testi

java.io. başarısız sonra Guava veya FileInputStream ile okuFileNotFoundException: o başıma tercüme

(Message benim pencere neden başka bir işlem tarafından kullanıldığı için Süreç İngilizce değil, dosyaya erişemiyor; "başka bir işlem" değinen yanıltıcı olduğunu, bunun Windows için normaldir çünkü Bu, açık engelleme ile doğrulanmış bir ben aynı işlem olsa bile bu anlat.)

Soru

nasıl uygulanır oluşturmak-sonra-adlandırmak son dosya, atomik yani görünür ya değildir, böylece Windows üzerinde Java kullanarak var ya da okunabilir?

Dosyaları teslim almaktan ziyade süreçleri kontrol ettiğimden, kullanımda herhangi bir özel okuma yöntemini veya Java içinde olduklarını bile edemiyorum. Bu nedenle çözüm, yukarıda listelenen tüm okuma yöntemleri ile çalışmalıdır.

+0

"Oku" (Guava ve FileInputStream) hangi yolla yazılır (NIO)? – SubOptimal

+0

Guava veya 'FileInputStream' ile' read' uygulandığında, kullanılan 'write' yönteminden bağımsız olarak rasgele olarak başarısız olur. 'read' NIO ile uygulandığında, kullanılan' write' yöntemine bakılmaksızın her zaman başarılı olur. (Aslında sanırım farklı "yazma" yöntemleri kaputun altında gerçekten farklı olmayabilir, çünkü her şey dosya yeniden adlandırılırken ya da - Windows API'da farklı adlandırma yöntemleri olabilir mi?) –

+0

Kullandığınız yeniden adlandırma, NIO okuma her zaman başarılı ve Guave/FileInputStream zaman zaman başarısız oluyor? – SubOptimal

cevap

0

Bu, Windows/NTFS'nin nasıl davrandığı gibi görünüyor. Ayrıca, eski IO ve NIO kullanan okumalar arasındaki davranışsal fark, farklı Windows API'ları kullanmaları olabilir.

Wikipedia on File locking dosya okuma/Windows API'leri yazma kullanan uygulamalar için

, bayt menzilli kilitleri tarafından (aynı zamanda zorunlu kilitleri anılacaktır) Windows içinden yürütme dosya sistemlerini uygulanmasını söylüyor. Windows dosya eşleme API'leri kullanmak uygulamalar için, bayt menzilli kilitleri zorlanmaz olan (aynı zamanda danışmanlık kilitler anılacaktır.)

Vikipedi Windows'un dokümanlar olmasa da, bu hala bazı ışık tutuyor.

(Seni çok takdir sadece aynı düşünme diğerleri böcek dokümanlara referanslarla bu. Gerçek cevapları, yazmak veya rapor zorunda kalmamak için bu cevabı koydu.) Hata raporu

+1

Windows'a özel tartışma burada: http://stackoverflow.com/questions/167414/is-an-atomic-file-rename-with-overwrite-possible-on-windows –

+1

Belki de [TxF - Transactional NTFS] (https://msdn.microsoft.com/en-us/library/hh802690%28v=vs.85%29.aspx) bağlantısının izlenmesi, davranışın NTFS'ye özgü olduğunu varsaymanızı kanıtlayabilir . – SubOptimal

+1

Belki bu da [golang-nuts haber grubunda tartışma] (https://groups.google.com/d/msg/golang-nuts/ZjRWB8bMhv4/BbTJCgfluegJ) – SubOptimal

0

yoktur JDK'da java.io.File.renameTo() işlevi için, Windows'ta atomik olmayan, Winde düzeltmesiyle kapatılan: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4017593. Bu yüzden probleminizi çözmek için muhtemelen temiz bir yöntem yoktur.

+0

Bu hata, hedef dosya zaten mevcut olduğunda "atomik yeniden adlandır değil" sorununu kapsar. Hedef dosya henüz bilinmediği biliniyorsa, daha basit bir durumda "atomik yeniden adlandır" ile ilgili sorum var. –

İlgili konular