Problema puntando ad un NAS con un java.io.File

di il
41 risposte

41 Risposte - Pagina 3

  • Re: Problema puntando ad un NAS con un java.io.File

    06/10/2025 - SpiritoLibero ha scritto:

    Secondo me il problema è proprio qui: perchè andare a rileggere tutta la directory per ogni file?

    ....

    4) Per ciascun oggetto della lista sorgente cerco la controparte nella lista destinazione: se non la trovo, l'elemento è da copiare, se la trovo verifico i timestamp e agisco di conseguenza.

    Il punto 4 è tutto lavoro che viene fatto in memoria (la sorgente è stata letta una sola volta e la destinazione pure).

    Amico caro questa è telepatia!  :-)
    Perché negli ultimi due giorni mi sono scervellato fino a sognarmi di notte di verificare in che ordine venivano elencati i file di origine rispetto a quelli di destinazione perché per velocizzare il tutto quelli di destinazione me li caricavo in un array ma eseguivo la verifica di un solo record perché pensavo fosse nello stesso ordine di quelli dell'origine. 
    Allora stamattina ho fatto una routine per visualizzare l'elenco dei file e ho scoperto che il listFiles di java.io.File li elenca in ordine alfabetico, mentre la list della DiskShare li elenca in un ordine strano che non è ne alfabetico ne in ordine di data di ultima modifica, per cui il mio test falliva e il programma eseguiva la copia anche di file non necessari.

    In questo processo mi occupo solo dei file perchè le directory andranno elaborate in modo ricorsivo.

    Certamente, la struttura per gestire le sottocartelle l'avevo già implementata in modo ricorsivo. ;-)

    Ovviamente, questo meccanismo si occupa solo di copiare i file nuovi/modificati da sorgente a destinazione, non va a fare considerazioni su eventuali file eliminati dalla sorgente.

    Infatti negli ultimi giorni ho modificato l'algoritmo per gestire anche la cancellazione dei file di destinazione che non sono presenti nelle cartelle sorgenti, in modo da allineare perfettamente il backup.

    Ho buttato giù un programmino di prova per verificare il discorso dei timestamp e non ho nessun tipo di problema: i file nuovi e modificati vengono correttamente copiati, mentre quelli non modificati (tra sorgente e destinazione) vengono saltati.

    Ecco a questo punto sarei curioso di sapere che tipo di struttura hai usato per memorizzare l'elenco dei file.
    Io ho usato una variabile di tipo ArrayList<String>.
    È la scelta corretta o c'è un modo più efficace e veloce?

  • Re: Problema puntando ad un NAS con un java.io.File

    L'elenco dei file nella directory sorgente me lo ritrovo in un array di oggetti File (uso il metodo list() della classe File per ottenerlo).

    L'elenco dei file nella directory di destinazione mi arriva inizialmente in List<FileIdBothDirectoryInformation>, che poi scorro per togliere di mezzo le directory e buttarli dentro un HashMap<String, FileIdBothDirectoryInformation> [chiave = nome del file]. In questo modo, quando poi andrò a scorrere l'array sorgente, posso usare il nome del file per indicizzare la mappa e ottenere il corrispondente FileIdBothDirectoryInformation (se esiste).

    Questo il pezzo di codice che effettua sta cosa (dstContent è il List<FileIdBothDirectoryInformation> che ottengo dal listing della directory di destinazione):

    // Costruisco una mappa di soli FILE (tralascio le directory) per andare bene a lavorarci su
    HashMap<String, FileIdBothDirectoryInformation> mappaFilesDst = new HashMap<>();
    for(FileIdBothDirectoryInformation entry : dstContent) {
        if (!EnumWithValue.EnumUtils.isSet(entry.getFileAttributes(), FileAttributes.FILE_ATTRIBUTE_DIRECTORY)) {
           // NON è una directory: lo aggiungo alla mappa
           mappaFilesDst.put(entry.getFileName(), entry);
        }    
    }

    Inizialmente avevo usato un TreeMap, ma poi mi sono reso conto che non mi serve l'ordinamento delle chiavi.

    Volendo si può ridurre tutto il ciclo usando gli stream:

    Map<String, FileIdBothDirectoryInformation> mappaFilesDst =
            dstContent.stream()
            .filter(e -> !EnumWithValue.EnumUtils.isSet(e.getFileAttributes(), FileAttributes.FILE_ATTRIBUTE_DIRECTORY))
            .collect(Collectors.toMap(FileIdBothDirectoryInformation::getFileName, Function.identity()));
  • Re: Problema puntando ad un NAS con un java.io.File

    Accipicchia! È completamente diverso da quello che ho fatto io!
    Mi rendo conto di quanto ho ancora da imparare su Java!

    Le prove che sto facendo su varie cartelle, anche con migliaia di file, sono parecchio veloci, per cui sono già contento.

    Poi vedrò di approfondire anche il tuo esempio.

    Grazie mille! ;-)

  • Re: Problema puntando ad un NAS con un java.io.File

    Caro SpiritoLibero, ho gridato vittoria troppo presto!

    Funziona tutto sui dischi collegati in USB e sui NAS, ma oggi ho provato ad eseguire il backup su un altro pc e sul portatile e..
    le copie di file nuovi o la sovrascrittura di file vecchi funziona, compresa la ricorsione delle sotto directory ma...
    quando deve cancellare un file o una cartella lo fa ma poi sta un po ad aspettare e mi da l'errore:
    Exception in thread "AWT-EventQueue-0" com.hierynomus.smbj.common.SMBRuntimeException: com.hierynomus.protocol.transport.TransportException: java.util.concurrent.ExecutionException: com.hierynomus.smbj.common.SMBRuntimeException: com.hierynomus.protocol.transport.TransportException: java.io.EOFException: EOF while reading packet

    Sul web ho trovato che può dipendere dall'impostazione del timeout, o da incorretta configurazione di Samba o differenti versioni tra client e server, ma allora mi chiedo: come mai le copie le fa e siccome i file di certe cartelle sono tanti, la connessione dura anche parecchio, per cui non mi capacito.

    Qualche dritta?

  • Re: Problema puntando ad un NAS con un java.io.File

    A me non è mai capitata quell'eccezione, quindi dritte al momento non ne ho, ma un suggerimento su cosa guardare (perchè, al momento, non so che giri fai).

    Mi viene da pensare ad una situazione di questo tipo (ma, appunto, sono totalmente nel campo delle ipotesi):

    1) Viene creato un oggetto com.hierynomus.smbj.share.File che punta al file da eliminare (per qualche motivo)

    2) Viene eliminato il file usando share.rm( path ), ma l'oggetto sopra indicato non è stato esplicitamente chiuso (con il metodo close())

    Se analizzando il tuo codice ti accorgi di una cosa simile, allora provvedi prima a chiudere l'oggetto com.hierynomus.smbj.share.File e poi procedi con l'eliminazione.

    Io ho avuto altri tipi di problemi in passato perchè, in una funzione, veniva creato un oggetto com.hierynomus.smbj.share.File, che poi non veniva esplicitamente chiuso. L'effetto che avevo io era un phantom file che non si riusciva ad eliminare finchè il programma non fosse stato completamente chiuso (con problemi a cascata sul resto delle applicazioni, ovviamente).

    Eventualmente posta il codice che usi per effettuare l'eliminazione del file, possibilmente anche con i controlli che fai per verificare che è da eliminare.

  • Re: Problema puntando ad un NAS con un java.io.File

    Allora, il problema della non chiusura del com.hierynomus.smbj.share.File non dovrebbe esserci perché la copia la faccio in un metodo apposta che apre e chiude il file, mentre le cancellazioni le faccio su file che non apro.

    Ho creato due metodi ridotti all'osso per mostrarti cosa faccio.
    il doBck riceve il DiskShare che apro nella routine chiamante e che poi passa a se stesso ricorsivamente, ma la parte ricorsiva non l'ho riportata perché entra in gioco solo per le directory.
    L'argomento fiOrig è il file della cartella da copiare e sDestPath contiene la cartella di destinazione.

      public void doBck(DiskShare ds, File fiOrig, String sDestPath) {
        long lOrigTime, lDestTime;
        String sDest;
        FileTime tDest;
        File fiFile;
    // Elimino dalla sDestPath i file inesistenti in fiOrig e aggiorno i file esistenti.
        for (FileIdBothDirectoryInformation fdi : ds.list(sDestPath, "*")) {
          if (!fdi.getFileName().equals(".") && !fdi.getFileName().equals("..")) {
            sDest = sDestPath + "/" + fdi.getFileName();
            fiFile = new File(fiOrig.getPath() + "/" + fdi.getFileName());
            if (fiFile.exists()) {
              if (fiFile.isFile()) {
                lOrigTime = (long)fiFile.lastModified();
                tDest = fdi.getChangeTime();
                lDestTime = (long)tDest.toEpochMillis();
                if ((lOrigTime - lDestTime) > 1000) {   
                  doCopy(ds, fiFile.getPath(), sDest);
                }
              }
            } else {                
              if (ds.fileExists(sDest)) {
                ds.rm(sDest);
              } else {         
                ds.rmdir(sDest, true);
              }
            }
          }
        }
      }
      public void doCopy(DiskShare ds, String sOrig, String sDest) {
        File fiOrig = new File(sOrig);
        com.hierynomus.smbj.share.File fiDest = ds.openFile(sDest, 
                      EnumSet.of(AccessMask.GENERIC_WRITE), null, SMB2ShareAccess.ALL,
                      SMB2CreateDisposition.FILE_OVERWRITE_IF,   
                      EnumSet.of(SMB2CreateOptions.FILE_RANDOM_ACCESS));
        OutputStream os = null;
        FileInputStream fis = null;
        try {
          fis = new FileInputStream(fiOrig);
          os = fiDest.getOutputStream();
          int byteReads = 0;
          byte[] buffer = new byte[4096];
          while((byteReads = fis.read(buffer)) >= 0) {
            os.write(buffer, 0, byteReads);
          }
          os.flush();
          os.close();
          fis.close();
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
          if (fis != null) try { fis.close(); } catch (Exception e) { }
          if (os != null) try { os.close(); } catch (Exception e) { }
        }
      }
    

    Spero si capisca.

  • Re: Problema puntando ad un NAS con un java.io.File

    Così ad occhio non vedo errori: l'unico mio dubbio riguarda questo pezzetto:

    if (ds.fileExists(sDest)) {
      ds.rm(sDest);
    } else {         
      ds.rmdir(sDest, true);
    }

    Il mio dubbio non riguarda né la logica, né la sintassi (sintatticamente è giusto e a livello logico è corretto perchè il percorso è stato costruito sulla base di un oggetto esistente nella destinazione, quindi se non esiste come file, allora deve logicamente essere una directory), ma riguarda la possibilità che l'oggetto FileIdBothDirectoryInformation non rappresenti né un file, né una directory ma qualcosa d'altro (un link? un device? un socket? boh...). Se così fosse (e non ho elementi per poter dire se così possa essere o no), quella logica non andrebbe più bene.

    Potrei arrivare a pensare anche che quell'oggetto rappresenti inizialmente un file e che, nel momento in cui lo vai a testare con ds.fileExists esso non esista già più (perchè rimosso da qualcos'altro... ma siamo nel campo delle ipotesi più disparate). Anche in questo caso la logica verrebbe scardinata.

    Ma al momento tralasciamo tutte queste ipotesi fantasiose.

    Qual è, di tutte quelle istruzioni, quella che genera l'eccezione?

  • Re: Problema puntando ad un NAS con un java.io.File

    10/10/2025 - SpiritoLibero ha scritto:

    ... ma riguarda la possibilità che l'oggetto FileIdBothDirectoryInformation non rappresenti né un file, né una directory ma qualcosa d'altro (un link? un device? un socket? boh...). Se così fosse (e non ho elementi per poter dire se così possa essere o no), quella logica non andrebbe più bene.

    Eh, bravo, anch'io ho avuto il dubbio che ci fosse un problema per i link e altri, perché ho visto che certi file di speciche applicazioni, come per esempio Thunderbird vengono sovrascritti senza che venga aggiornata la data di modifica, per cui se rieseguo la procedura vengono continuamente sovrascritti. 
    Però per lo meno in quei casi non mi da alcun errore.

    Potrei arrivare a pensare anche che quell'oggetto rappresenti inizialmente un file e che, nel momento in cui lo vai a testare con ds.fileExists esso non esista già più (perchè rimosso da qualcos'altro... ma siamo nel campo delle ipotesi più disparate). Anche in questo caso la logica verrebbe scardinata.

    Ma, nel mio caso questa situazione non dovrebbe sussistere, perché quelli che copio non sono file di sistema, ma tutti file di un disco dati a cui accedo solo tramite comandi e applicazioni che eseguo io.

    Qual è, di tutte quelle istruzioni, quella che genera l'eccezione?

    Il problema è sia sulla cancellazione di file quindi sulla ds.rm che delle directory ovvero sulla ds.rmdir.
    Tra l'altro preciso che quando cerca di cancellare una directory, la svuota dei file contenuti ma non cancella la directory stessa, mentre invece un singolo file lo cancella.
    In entrambe i casi dopo circa un minuto di attesa da l'errore suddetto.

  • Re: Problema puntando ad un NAS con un java.io.File

    Cercando online possibili cause a questo problema sono capitato qui:

    https://github.com/hierynomus/smbj/issues/653

    E' la sezione issues del progetto SMBJ.

    Io farei un tentativo di regressione della libreria SMBJ dalla versione 0.13.0 alla 0.12.0. Vuoi mai che abbiano re-introdotto qualche cosa nella nuova versione che ha fatto riemergere quel vecchio bug...

  • Re: Problema puntando ad un NAS con un java.io.File

    Ho scaricato il file smbj-0.12.2.jar 

    In Netbeans ho provato a sostituirlo alla versione 13 ma non è cambiato nulla.

    Magari bisogna sostituire anche le relative dipendenze... ma mi vergogno di me stesso perché non riesco più a trovare dove scaricare le relative librerie !

  • Re: Problema puntando ad un NAS con un java.io.File

    Aggiornamento.

    Sono poi riuscito a trovare i file da scaricare e ho provato anche con la versione più recente, ma non è cambiato nulla! 

    Ho quindi inserito una issue sul sito github descrivendo il problema, ma al momento nessuno mi ha risposto. 

    Sono nella disperazione totale. :-((

  • Re: Problema puntando ad un NAS con un java.io.File

    Se qualcuno volesse provare a riprodurre la situazione ho creato un programmino molto semplice.
    Basta adeguare i parametri dell'Indirizzo IP del PC, lo username e password, la cartella della DiskShare e il percorso e nome del file.

    package filedelete;
    
    import com.hierynomus.smbj.SMBClient;
    import com.hierynomus.smbj.auth.AuthenticationContext;
    import com.hierynomus.smbj.connection.Connection;
    import com.hierynomus.smbj.session.Session;
    import com.hierynomus.smbj.share.DiskShare;
    import java.io.IOException;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class FileDelete {
    
      static void DelFile() {
        SMBClient client = new SMBClient();
        try (Connection cnt = client.connect("192.168.1.2")) {
          AuthenticationContext ac = new AuthenticationContext("root", "rootpassword".toCharArray(), "");
          Session ses = cnt.authenticate(ac);
          try (DiskShare ds = (DiskShare) ses.connectShare("Dati")) {
            ds.rm("/Temp/Pippo.txt");
          }
        } catch (IOException ex) {
          Logger.getLogger(FileDelete.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
    
      public static void main(String[] args) {
        DelFile();
      }
    }
Devi accedere o registrarti per scrivere nel forum
41 risposte