2009-09-07 32 views
38

Necesito escribir un renombrador de archivo por lotes personalizado. Tengo la mayor parte de esto hecho, excepto que no puedo averiguar cómo verificar si un archivo ya está abierto. Solo estoy usando el paquete java.io.File y hay un método canWrite(), pero eso no parece probar si el archivo está siendo utilizado por otro programa. ¿Alguna idea sobre cómo puedo hacer que esto funcione?Compruebe si el archivo ya está abierto

+0

Esta es dependiente de la plataforma. ¿Para qué plataforma es esto? – skaffman

+0

Windows 2003 Server –

+0

Acabo de repetir su pregunta porque podría tener que hacer eso llamando a WinAPI usando JNI o ​​JNA. –

Respuesta

1

No creo que alguna vez consigas una solución definitiva para esto, el sistema operativo no necesariamente te dirá si el archivo está abierto o no.

Es posible que obtenga un poco de kilometraje de java.nio.channels.FileLock, aunque el javadoc está cargado de advertencias.

+0

Sí, no hay garantía de que el sistema operativo subyacente, o incluso el sistema de archivos, admita bloqueos de archivos de forma razonable. – aperkins

5

Su mejor opción es establecer un bloqueo exclusivo en el archivo. Si el archivo está abierto por otros procesos, obtendrá una excepción. Por ejemplo,

File file = new File(fileName); 
FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); 
// Get an exclusive lock on the whole file 
FileLock lock = channel.lock(); 
try { 
    lock = channel.tryLock(); 
    // Ok. You get the lock 
} catch (OverlappingFileLockException e) { 
    // File is open by someone else 
} finally { 
    lock.release(); 
} 
+2

Es una buena idea a primera vista, pero: acabo de ejecutar este código modificado con tres archivos diferentes: un archivo de Word, un archivo de Excel y un archivo de texto abierto con UltraEdit. Solo el archivo de texto causa OverlappingFileLockException. Los primeros dos causaron una excepción FileNotFoundException en RandomAccessFile.open(). Tendría que mirar getMessage() para determinar que la causa es que el archivo está bloqueado, lo cual es malo. Muy malo si tenemos en cuenta que el mensaje está localizado en el SO (está en alemán en mi máquina). –

+0

Puede usar file.exists() para evitar bloquear recursos inexistentes. Y parece que solo necesitas uno: lock() o tryLock(). –

+0

Estoy de acuerdo con serge_bg: doing tryLock() después de que lock() arroje una excepción OverlappingFileLockException. Además, el lock.release() debería ser un nivel más alto. Finalmente, ¿no es necesario que el canal también esté cerrado? –

12

(El Q & A es acerca de cómo lidiar con las cerraduras de Windows "abrir archivo" ... no cómo implementar este tipo de bloqueo de forma portátil.)

Todo este asunto está cargado de problemas de portabilidad y condiciones de carrera:

  • Puede intentar usar FileLock, pero no es necesariamente compatible con su sistema operativo y/o sistema de archivos.
  • Parece que en Windows es posible que no pueda usar FileLock si otra aplicación ha abierto el archivo de una manera particular.
  • Incluso si logró usar FileLock o alguna otra cosa, todavía tiene el problema de que algo puede entrar y abrir el archivo entre la prueba del archivo y el cambio de nombre.

Una solución más simple y (probablemente) más robusta es intentar el cambio de nombre (o lo que sea que esté intentando hacer) y diagnosticar el valor de retorno y/o cualquier excepción de Java que surja debido a archivos abiertos.

Notas:

  1. si utiliza la API Files lugar de la API File obtendrá más información en el caso de un fallo.

  2. En los sistemas donde se le permite cambiar el nombre (o lo que sea) de un archivo bloqueado o abierto, no obtendrá ningún resultado de falla o excepciones. La operación solo tendrá éxito.

+0

Podría estar equivocado, pero creo que en algunos sistemas de archivos (no tan exóticos) incluso puede mover el archivo mientras se escribe en él. –

+0

@ JaroslavZáruba - ¿Cómo se relaciona eso con la pregunta? o mi respuesta? –

+0

"intente cambiar el nombre ... y diagnosticar cualquier excepción de Java" - esto está lejos de ser robusto, en Linux FS típico no obtendrá ninguna exc eption –

21

El uso de la biblioteca Apache Commons IO ...

boolean isFileUnlocked = false; 
try { 
    org.apache.commons.io.FileUtils.touch(yourFile); 
    isFileUnlocked = true; 
} catch (IOException e) { 
    isFileUnlocked = false; 
} 

if(isFileUnlocked){ 
    // Do stuff you need to do with a file that is NOT locked. 
} else { 
    // Do stuff you need to do with a file that IS locked 
} 
+0

Esto no arroja ningún tipo de excepción para un archivo .xls, lo toca muy bien. – WillBD

+11

¡Alerta de condición de carrera! –

+0

Esto cambiaría la última fecha y hora de modificación del archivo. – ssp

4
// TO CHECK WHETHER A FILE IS OPENED 
    // OR NOT (not for .txt files) 

    // the file we want to check 
    String fileName = "C:\\Text.xlsx"; 
    File file = new File(fileName); 

    // try to rename the file with the same name 
    File sameFileName = new File(fileName); 

    if(file.renameTo(sameFileName)){ 
     // if the file is renamed 
     System.out.println("file is closed");  
    }else{ 
     // if the file didnt accept the renaming operation 
     System.out.println("file is opened"); 
    } 
+1

¡Simplemente genio! +1. –

+2

No funcionará para Linux. En los sistemas de archivos POSIX, el nombre del archivo no es la clave principal, sino un enlace al contenido del archivo. Por lo tanto, puede cambiar el nombre o eliminar el archivo abierto. Nadie obtiene ningún error. –

+2

Puede confirmar esto funciona para Windows10. Parece mucho más elegante que provocar una excepción. No funciona en Linux con ext4. – JosefScript

-3

Si el archivo está en uso FileOutputStream fileOutputStream = new FileOutputStream(file); vuelve java.io.FileNotFoundException con 'El proceso no tiene acceso al archivo porque está siendo utilizado por otro proceso 'en el mensaje de excepción.

+0

Agradecería los comentarios sobre los motivos de los votos a la baja – Aikerima

+0

Sobrescribió el contenido del archivo con cero bytes si el archivo no estaba en uso, por lo que básicamente eliminó el archivo. Se aplica a Linux (ext4) y Win10. El tercer vástago fue mío. – JosefScript

+0

Tengo esta excepción cuando uso _FileOutputStream fileOutputStream = new FileOutputStream (archivo) _ –

2

org.apache.commons.io.FileUtils.touch(yourFile) no comprueba si su archivo está abierto o no. En cambio, cambia la marca de tiempo del archivo a la hora actual.

que utilizan IOException y funciona muy bien:

try 
{ 
    String filePath = "C:\sheet.xlsx"; 
    FileWriter fw = new FileWriter(filePath);     
} 
catch (IOException e) 
{ 
    System.out.println("File is open"); 
} 
+0

El uso de excepciones para el flujo de control normal se considera una práctica deficiente. Además, puede terminar dejando abierto 'fw' en el caso que no genere una excepción. –

+0

para que pueda usar finalmente para que el archivo se cierre de todas formas FileWriter fw; try { String filePath = "C: \ sheet.xlsx"; fw = new FileWriter (filePath); } catch (IOException e) { System.out.println ("El archivo está abierto"); } finalmente { fw.close(); } – Ali

Cuestiones relacionadas