2013-03-20 17 views
6

Ho seguito il tutorialper le modifiche Java7 nio2 per monitorare in modo ricorsivo l'intero contenuto di una directory utilizzando il codice dell'esempio WatchDir.java.Java 7 WatchService - Il processo non può accedere al file perché è utilizzato da un altro processo

Il codice simile a questo:

// Get list of events for the watch key. 
for (WatchEvent<?> event : key.pollEvents()) { 
// This key is registered only for ENTRY_CREATE events, but an OVERFLOW event 
// can occur regardless if events are lost or discarded. 
if (event.kind() == OVERFLOW) { 
    continue; 
} 

// Context for directory entry event is the file name of entry. 
@SuppressWarnings("unchecked") 
WatchEvent<Path> ev = (WatchEvent<Path>)event; 
Path fileName = ev.context(); 
Path fullPath = dir.resolve(fileName); 

try { 
    // Print out event. 
    System.out.print("Processing file: " + fileName); 

    processed = fileProcessor.processFile(fullPath); 

    System.out.println("Processed = " + processed); 

    if (processed) { 
     // Print out event. 
     System.out.println(" - Done!"); 
    } 
} 
catch (FileNotFoundException e) { 
    System.err.println("Error message: " + e.getMessage()); 
} 
catch (IOException e) { 
    System.err.println("Error processing file: " + fileName.toString()); 
    System.err.println("Error message: " + e.getMessage()); 
} 

Ok, quindi il problema (dove sono sicuro che fare qualcosa di stupido) è qui:

processed = fileProcessor.processFile(fullPath); 

E ciò che fa è qualcosa di simile a questo:

public synchronized boolean processFile(Path fullPath) throws IOException { 
String line; 
String[] tokens; 
String fileName = fullPath.getFileName().toString(); 
String fullPathFileName = fullPath.toString(); 

// Create the file. 
File sourceFile = new File(fullPath.toString()); 

// If the file does not exist, print out an error message and return. 
if (sourceFile.exists() == false) { 
    System.err.println("ERROR: " + fullPathFileName + ": No such file"); 
    return false; 
} 

// Check file extension. 
if (!getFileExtension(fullPathFileName).equalsIgnoreCase("dat")) { 
    System.out.println(" - Ignored."); 
    return false; 
} 

// Process source file. 
try (BufferedReader bReader = new BufferedReader(new FileReader(sourceFile))) { 
    int type; 

    // Process each line of the file. 
    while (bReader.ready()) { 

     // Get a single line. 
     line = bReader.readLine(); 

     // Get line tokens. 
     tokens = line.split(delimiter); 

     // Get type. 
     type = Integer.parseInt(tokens[0]); 

     switch (type) { 
     // Type 1 = Salesman. 
     case 1: 
      -> Call static method to process tokes. 
      break; 
     // Type 2 = Customer. 
     case 2: 
      -> Call static method to process tokes. 
      break; 
     // Type 3 = Sales. 
     case 3: 
      -> Call static method to process tokes. 
      break; 
     // Other types are unknown! 
     default: 
      System.err.println("Unknown type: " + type); 
      break; 
     } 
    } 

    PrintStream ps = null; 
    try { 

     // Write output file. 
     // Doesn't matter. 

    } 
    finally {    
     if (ps != null) { 
      ps.close(); 
     } 
    } 
    return true; 
} 
} 

La prima volta che gestisco un evento, tutto funziona correttamente! Anche se c'è più di 1 file da elaborare. Ma nelle volte consecutive, ottengo questo messaggio di errore:

Il processo non può accedere al file perché è utilizzato da un altro processo

Che cosa sto facendo male qui? Cosa posso fare per elaborare i file consecutivi con successo?

Due note importanti che ho dimenticato di dire:

  1. Sto usando Windows 7.
  2. Quando eseguo l'applicazione in modalità di debug, funziona.

EDIT: Se posso aggiungere un sonno prima che io cerco di utilizzare il file, funziona:

Thread.sleep(500); 

// Process source file. 
try (BufferedReader bReader = new BufferedReader(new FileReader(sourceFile))) { 

Quindi, è possibile essere Windows che non è lo sblocco del file in tempo? Come posso risolverlo (in modo corretto)?

risposta

14

Ok, ho trovato una soluzione. Non so se è il modo migliore per farlo, ma funziona. Sfortunatamente, file.canRead() e file.canWrite() restituiscono entrambi true, anche se il file è ancora bloccato da Windows. Così ho scoperto che se provassi a "rinominarlo" con lo stesso nome, so se Windows ci sta lavorando oppure no. Quindi questo è quello che ho fatto:

while(!sourceFile.renameTo(sourceFile)) { 
     // Cannot read from file, windows still working on it. 
     Thread.sleep(10); 
    } 
+0

funziona :) –

+1

Bel trucco! Mi ha aiutato! –

1

Ho avuto il problema simile. Ma il mio file non viene bloccato quando viene copiato. Se avessi cercato di leggerlo subito dopo aver creato l'evento che era vuoto.

Penso che nel tuo caso non ci siano trucchi e puoi semplicemente gestire l'errore e se ottieni un errore puoi eseguire il sonno e riprovare dopo. Ma per me non è giusto per me. Non ho errore Così ho provato la tua solitaria e ho capito che non è accettabile anche per me perché non ho i diritti tranne la lettura. Quindi posso suggerire il mio "trucco". Sto utilizzando la classe java.io.RandomAccessFile:

List<String> lines = null; 

while(true){ 
    try(RandomAccessFile raf = new RandomAccessFile(fileFullPath.toFile() , "r")){ 
     lines = Files.readAllLines(fileFullPath , Charset.forName(ENCODING)); 
     break; 
    }catch(FileNotFoundException ex){ 
     LOG.warn("File isn't yet completed: {}",ex.getMessage()); 
     LOG.info("Waiting {} for next attempt to read it" , SLEEP_INTERVAL_READING); 
     try { 
      Thread.sleep(SLEEP_INTERVAL_READING); 
     } catch (InterruptedException e) { 
      LOG.error("Waiting was interrupted",e); 
     } 
    }catch(Exception ex){ 
     LOG.error("Error in reading file ",ex); 
     clear(); 
     return false; 
    } 
} 
0

quando l'evento è di tipo ENTRY_MODIFY permettere al thread corrente a dormire per un paio di secondi. Non posso dire esattamente perché questo sta accadendo, ma qualche volta più eventi ENTRY_MODIFY si stanno stampando nei miei registri. A seconda delle dimensioni del file, impostare l'ora di spegnimento.

if(kind == StandardWatchEventKinds.ENTRY_MODIFY){ 
try { 
    Thread.sleep(1000); 
} catch (InterruptedException e) { 
    e.printStackTrace(); 
} 
//toDo - something 
}