IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Calcul du md5 de millions de fichiers


Sujet :

Java

  1. #1
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut Calcul du md5 de millions de fichiers
    Et oui c'est encore moi >< xD

    J'ai effectué un programme qui parcourt entre 5 000 000 et 10 000 000 de fichier, et qui en fait une signature (MD5).
    Le programme s'exécute sur une machine red hat.
    J'ai donc créé une méthode qui exécute un commande linux : "md5sum /test/test/test" et je récupère le retour.
    Je voulais savoir s’il y avait possibilité d'améliorer les performances de cette méthode, ou si il y a un tout autre moyen de faire ?
    Merci d'avance,
    Voilà ma méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public static final String hashFile(String path) {
            String hashToReturn = "";
            try {
                String algo = Constants.getHashAlgorithm(); // return "md5"
                String[] tmpPath = path.split(" ");
                String pathToUse = tmpPath[0];
                String cmd = algo + "sum -b " + pathToUse;
                Process p = Runtime.getRuntime().exec(cmd);
                p.waitFor();
                BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
     
                String line;
                String tmp[];
                while ((line = reader.readLine()) != null) {
                    tmp = line.split(" ");
                    hashToReturn = tmp[0];
                }
                reader.close();
                p.destroy();
            } catch (Exception e) {
                System.out.println("Problem hashing file ! (" + path + ")");
                if (hashToReturn.equals("")) {
                    hashToReturn = "notHashed";
                }
            }
            return hashToReturn;
        }
    Merci !
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  2. #2
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Personne n'a d'idée ? :'(

    J'vais alors réduire ma question,

    Pensez-vous qu'il est plus rapide d'exécuter md5sum (Commande Shell), ou alors de coder la méthode de hachage ?
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  3. #3
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Vu le nombre de fichier, ca serait surement mieux de coder la fonction et le faire toi meme...

  4. #4
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Salut,

    Merci pour ta réponse.
    Donc tu penses que ça serrait plus performant. Hum okey
    Mais comment dev ça en Java ? Là c'est une autre histoire ^^
    Mais ya surement une lib qui fait ça non ?
    Ou en tout cas ya déjà quelqu'un qui l'a fait je suppose ^^
    Allé ça mérite une petite recherche
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  5. #5
    Futur Membre du Club
    Homme Profil pro
    bioinformaticien
    Inscrit en
    Septembre 2014
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : Suisse

    Informations professionnelles :
    Activité : bioinformaticien

    Informations forums :
    Inscription : Septembre 2014
    Messages : 1
    Points : 6
    Points
    6
    Par défaut
    Salut,

    Citation Envoyé par Sennad Voir le message
    Salut,

    Merci pour ta réponse.
    Donc tu penses que ça serrait plus performant. Hum okey
    Mais comment dev ça en Java ? Là c'est une autre histoire ^^
    Mais ya surement une lib qui fait ça non ?
    Ou en tout cas ya déjà quelqu'un qui l'a fait je suppose ^^
    Allé ça mérite une petite recherche
    Une DigestInputStream construite avec un MessageDigest.getInstance("MD5") fonctionne bien et me donne des performance similaires comparé à md5sum.
    Un coup de google donne plein d'exemples.

  6. #6
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Salut,

    Merci beaucoup pour ta réponse, je vais regarder ça
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  7. #7
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Tout d'abord j'aurais quelque remarque sur le code, car le traitement du process est mauvais et risque de poser des deadlocks.

    • Tu ne fermes pas les flux stdin/stderr du process, même si tu ne les utilisent pas.
      Si par malheur le process les utilisent il risque de se retrouver bloqué...
      Il faut les fermer explicitement si tu ne les utilisent pas.
    • Tu attends la fin du process, avant de lire son flux.
      Tu as de la chance que md5sum ne retourne qu'une petite chaine car ca passe.
      Mais si le programme écrit trop de donnée dans son stdout, cela risque de bloquer car le flux entre les deux process serait plein.
      En clair tu serais bloqué dans le waitFor() pendant que le process serait bloqué en écriture en attendant que tu traites le flux.
      Traites les flux en priorité. Le waitFor() uniquement à la fin une fois que tous les flux ont été fermées.
    • Il manque des try/finally (ou des try-with-ressource sous Java 7) pour la libération des ressources.
      C'est très important surtout si tu veux exécuter cela plusieurs milliers de fois. Une petite erreur pourrait bloquer toute l'application voir le système...



    Bref l'appel du process devrait plutôt ressembler à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    	Process p = Runtime.getRuntime().exec(cmd);
    	try {
    		// 1. On ferme les flux inutile :
    		p.getOutputStream().close();
    		p.getErrorStream().close();
     
    		// 2. On lit les données :
    		BufferedReader reader = new BufferedReader(new InputStreamReader(
    				p.getInputStream()));
    		try {
    			String line;
    			String tmp[];
    			while ((line = reader.readLine()) != null) {
    				tmp = line.split(" ");
    				hashToReturn = tmp[0];
    			}
    		} finally {
    			reader.close();
    		}
     
    		// 3. on attend la fin du process
    		// (ici c'est inutile car la fin du flux indique la fin du process)
    		p.waitFor();
     
    	} finally {
    		p.destroy();
    	}


    Sinon oui : faire un md5 directement sera sûrement plus rapide que d'appeler un programme externe...
    Le code étant relativement simple à écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    	public static final String md5(File file) throws FileNotFoundException, IOException {
    		FileInputStream input = new FileInputStream(file);
    		try {
    			return md5(input);
    		} finally {
    			input.close();
    		}
    	}
     
    	public static final String md5(InputStream input) throws IOException {
    		try {
    			// 1. On crée un instance de MessageDigest en MD5 :
    			MessageDigest md = MessageDigest.getInstance("md5");
    			// 2. On y copie la totalité du flux :
    			byte[] buf = new byte[8192];
    			int len;
    			while ( (len=input.read(buf)) > 0) {
    				md.update(buf, 0, len);
    			}
    			// 3. On récupère le résultat :
    			byte[] digest = md.digest();
     
    			// 4. Que l'on convertie en une chaine lisible :
    			StringBuffer sb = new StringBuffer(digest.length * 2);
    			for (byte b : digest) {
    				sb.append(String.format("%02x", b & 0xff));
    			}
    			return sb.toString();
    		} catch (NoSuchAlgorithmException e) {
    			// Ceci ne devrait jamais arrivé car le MD5
    			// est supporté par toutes les JVMs :
    			throw new IllegalStateException(e);
    		}
    	}

    a++

  8. #8
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Salut !!

    Waw merci pour ta réponse très intéressante !
    J'ai appris quelques trucs Merci, je vais essayer ça et voir ce que ça donne, je reviens vers vous dans pas longtemps
    Je vois qu'il dois y avoir beaucoup de petits trucs comme ça dans mon code, qui font que mon appli est super longue..
    Mais j'ose pas balancer un gros patté de code et vous demander ce que je peux améliorer.. Sinon vous allez me taper dessus

    En tout cas merci beaucoup !
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  9. #9
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Yeah c'est super, la méthode est environ 2 fois plus rapide ! Génial ! Je vais gagné 1 semaine d’exécution !
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  10. #10
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Vu l'efficacité, et le temps gagné grâce à toi, je pose un ma méthode qui parcourt les fichiers et qui en fait le hash.
    En fait c'est un peu plus compliqué que ça, grâce au hash, je détermine si le fichier a été modifié depuis la dernière fois que le programme est passé.
    Je peux aussi détecter un nouveau fichier du coup.

    J'aimerai savoir si il y a des erreurs importantes dans mon code, ou de quoi optimiser.. Merci d'avance !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    public class JdbcReferencesFilesDAO implements IReferencesFilesDAO {
     
        @Override
        public void parseDirectory(String path, Connection conn) {
            File actual = new File(path);
            String actualPath;
            String actualName;
            String hash;
            String oldHash;
            try {
                for (File f : actual.listFiles()) {
                    actualPath = f.getAbsolutePath();
                    actualName = f.getName();
                    if (f.isDirectory() && !f.getName().equals(".snapshot")) {
                        parseDirectory(actualPath, conn);
                    } else if (!f.getName().equals(".snapshot")) {
                        Constants.counter++;
                        hash = Utils.HashUtils.hashFile(actualPath);
                        oldHash = getOldHashIfExist(actualPath, conn);
     
                        if (oldHash != null) {
                            if (oldHash.equals(hash)) {
                                updateCheckedColumn(actualPath, conn);
                            } else {
                                Utils.ReportUtils.WriteInFile("Reference File MODIFIED : " + actualPath, Constants.REFERENCES_REPORTING_PATH);
                                updateSignatureAndCheckedColumn(hash, actualPath, conn);
                            }
                        } else {
                            Utils.ReportUtils.WriteInFile("Reference File ADDED : " + actualPath, Constants.REFERENCES_REPORTING_PATH);
                            insertInReferencesFilesTableDB(actualName, actualPath, hash, conn);
                        }
                        System.out.print(".");
                        if (((Constants.counter) % 100) == 0) {
                            System.out.print("\n" + Constants.counter + "files found");
                        }
                    }
                    System.gc();
                }
            } catch (NullPointerException e) {
                System.out.println("      Problem on " + path);
                Utils.ReportUtils.WriteInFile("Problem on " + path, Constants.LOGS_PATH);
            }
        }
     
        @Override
        public void insertInReferencesFilesTableDB(String name, String url, String hash, Connection conn) {
            try {
                conn.setAutoCommit(false);
                PreparedStatement pstmt = conn.prepareStatement("INSERT INTO ReferencesFiles (Name, URL, Signature, Checked) VALUES (?,?,?,?)");
                pstmt.setString(1, name);
                pstmt.setString(2, url);
                pstmt.setString(3, hash);
                pstmt.setInt(4, 1);
                pstmt.executeUpdate();
                conn.commit();
                pstmt.close();
            } catch (SQLException e) {
                System.out.println("      Impossible to add " + name + " in ReferencesFiles Table !");
                Utils.ReportUtils.WriteInFile("Problem adding " + url + " file", Constants.LOGS_PATH);
            }
        }
     
        @Override
        public String getOldHashIfExist(String path, Connection conn) {
            try {
                PreparedStatement pstmt = conn.prepareStatement("SELECT Signature FROM ReferencesFiles WHERE URL=?");
                pstmt.setString(1, path);
                ResultSet rs = pstmt.executeQuery();
                if (rs.first()) {
                    String hash = rs.getString(1);
                    pstmt.close();
                    rs.close();
                    return hash;
                } else {
                    pstmt.close();
                    rs.close();
                    return null;
                }
            } catch (Exception e) {
                System.out.println("Problem checking if file of reference exist in DB (" + path + ")");
                Utils.ReportUtils.WriteInFile("Problem checking if file of reference exist in DB (" + path + ")", Constants.LOGS_PATH);
                return null;
            }
        }
     
        @Override
        public void updateCheckedColumn(String url, Connection conn) {
            try {
                conn.setAutoCommit(false);
                PreparedStatement pstmt = conn.prepareStatement("UPDATE ReferencesFiles SET Checked=1 WHERE URL=?");
                pstmt.setString(1, url);
                pstmt.executeUpdate();
                conn.commit();
                pstmt.close();
            } catch (SQLException e) {
                System.out.println("Problem updating last checked date (" + url + ")");
            }
        }
     
        @Override
        public void updateSignatureAndCheckedColumn(String url, String newHash, Connection conn) {
            try {
                conn.setAutoCommit(false);
                PreparedStatement pstmt = conn.prepareStatement("UPDATE ReferencesFiles SET Signature=?, Checked=1 WHERE URL=?");
                pstmt.setString(1, newHash);
                pstmt.setString(2, url);
                pstmt.executeUpdate();
                conn.commit();
                pstmt.close();
            } catch (SQLException e) {
                System.out.println("Problem updating signature (" + url + ")");
            }
        }
     
        @Override
        public void checkDeletedFiles(Connection conn) {
     
            try {
                PreparedStatement pstmt = conn.prepareStatement("SELECT URL FROM ReferencesFiles WHERE (Checked = 0)");
                ResultSet rs = pstmt.executeQuery();
                while (rs.next()) {
                    System.out.println("!!! Reference DELETED !!! -> " + rs.getString(1));
                    deleteRecord(rs.getString(1), conn);
                    Utils.ReportUtils.WriteInFile("Reference File DELETED " + rs.getString(1), Constants.REFERENCES_REPORTING_PATH);
                }
                rs.close();
                pstmt.close();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Problem checking deletedFiles");
                Utils.ReportUtils.WriteInFile("Problem checking deleted files", Constants.LOGS_PATH);
            }
        }
     
        @Override
        public void deleteRecord(String path, Connection conn) {
            try {
                conn.setAutoCommit(false);
                PreparedStatement pstmt = conn.prepareStatement("DELETE FROM ReferencesFiles WHERE URL = ?");
                pstmt.setString(1, path);
                pstmt.executeUpdate();
                conn.commit();
                pstmt.close();
            } catch (SQLException e) {
                System.out.println("Problem deleting " + path);
                Utils.ReportUtils.WriteInFile("Problem deleting " + path, Constants.LOGS_PATH);
            }
        }
    }
    Désolé pour ce patté...
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  11. #11
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Citation Envoyé par Sennad Voir le message
    J'aimerai savoir si il y a des erreurs importantes dans mon code, ou de quoi optimiser..
    Utiliser System.gc();, c'est moche

    Sinon, je remarque que tu utilises beaucoup la BDD (plusieurs fois par fichier). Je ne sais pas laquelle tu utilises mais si tu fais des acces sur des millions de fichiers, en raffraichissant 1 par 1, ca peut etre pas mal de faire des traitements par lots.

  12. #12
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Salut !

    D'abord merci pour ta réponse =)
    Pour le System.gc(), c'est moche ok, mais du coup je le vire ? ou j'utilise une autre méthode ? je me suis dis que ça ferait toujours du bien, mais ça sert peut être a rien je sais pas !
    Pour le traitement par lots j'y ai pensé en postant le code et en le relisant, je vais peut être faire ça ouais ça serrait pas mal
    ( j'utilise MySQL avec InnoDB )
    Merci encore !
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  13. #13
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Avant tout une petite remarque : c'est normal que tu ignores toutes les exceptions d'un point de vue logiciel ?
    Je veux dire, à chaque fois qu'une exception survient tu la catches, tu logges, mais tu continues le programme comme si de rien n'était. C'est normal ?


    Sinon avant de parler optimisation, il faut commencer par faire un code "propre".
    Je vois quelques soucis :

    • Ne jamais appeler System.gc() dans une "vrai" application ! Et encore moins dans une boucle !!!
      Au mieux c'est ignoré... mais sinon cela ne fait que surcharger inutilement le travail du GC ! Dans une boucle cela peut être infernal (exécute ton code avec -verbose:gc pour t'en convaincre).
      A mon avis une grosse partie de tes lenteurs viennent de là...

    • Sinon toujours la même chose : tu ne libères pas proprement tes ressources (Statement, ResultSet).
      Si une exception survient pendant le traitement les ressources en question resteront ouvertes pour rien...
      Il faut utiliser un try/finally par ressource (ou le try-with-ressource de Java 7).
      Ce n'est pas optionnel ou si on veut... il faut le faire !

    • A quoi servent les setAutoCommit(false) ? Ils sont toujours suivi d'une seule opération et d'un commit()...






    Après sinon coté optimisation je vois 3 petits points :
    • Éviter les doubles-vérifications, surtout dans les boucles.
      Exemple :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      if (f.isDirectory() && !f.getName().equals(".snapshot")) {
      	// CODE 1
      } else if (!f.getName().equals(".snapshot")) {
      	// CODE 2
      }
      Devrait plutôt s'écrire comme ceci :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      if (!f.getName().equals(".snapshot")) {
      	if (f.isDirectory()) {
      		// CODE 1
      	} else {
      		// CODE 2
      	}	
      }
      La différence ne doit pas être énorme, mais sur des milliers de fichiers ca peut faire quelques ms...

    • Réutiliser les PreparedStatement.
      C'est à dire les déclarer (et les libérer) dans la méthode parseDirectory() en les conservant pour toute la durée du traitement.
      Cela évites au drivers JDBC de reparser le SQL pour chaque nouvelle requête.

    • Une fois qu'on a réutiliser le même PreparedStatement, on peut envisager l'envoi par batch des update/insert.
      C'est à dire en utilisant addBatch() à la place de executeUpdate(), puis en utilisant executeBatch() à intervalle régulier pour mettre à jour les données... (mais pas trop grand l'intervalle sinon on consomme beaucoup de mémoire)


    Attention toutefois car ces deux dernier point vont alourdir un peu le code, donc ce serait plutôt à faire en dernier moment si les perfs sont toujours mauvaise (à mon avis le System.gc() doit déjà faire bien mal).
    De plus cela s'adapte assez mal avec le coté récursif du code, et dans ce cas il serait sûrement souhaitable d'envisager une version itérative...


    a++

  14. #14
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Super !! Merci énormément d'avoir pris le temps de répondre et tout et tout, sachant que j'ai laissé un pavé de code comme ça, c'est vraiment gentil de ta part.

    Un grand grand merci à toi, tes explications sont claires et très intéressantes !
    J'aime ta façon de répondre, d'autres m'aurait rabaissé en disant "mais tu fais de la m.... ! C’est pas comme ça qu'il faut faire !" enfin je pense qu'on a compris ce que je veux dire !

    Pour les exceptions, oui c'est normal, parce-que mon programme doit parcourir plusieurs répertoires donnés, et ça toute les semaine (C'est pour ça que ça doit tenir sur une semaine max) et si il s’arrête, personne ne sera derrière pour le remarquer et le relancer, puis avec tout ce qui a il irai jamais jusqu'au bout :/

    En tout cas merci beaucoup, je vais arranger tout ça
    (Gros gros gros +1, j’espère que d'autres personnes en mettrons même si tu en a plus trop besoin )
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  15. #15
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Voilà, j'ai fini d'arranger tout ça, est-ce correct ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    public class JdbcReferencesFilesDAO implements IReferencesFilesDAO {
     
        @Override
        public void parseDirectory(String path, Connection conn) {
            File actual = new File(path);
            String actualPath;
            String actualName;
            String hash;
            String oldHash;
            PreparedStatement pstmt = null;
     
            try {
                for (File f : actual.listFiles()) {
                    actualPath = f.getAbsolutePath();
                    actualName = f.getName();
                    if (!f.getName().equals(".snapshot")) {
                        if (f.isDirectory()) {
                            parseDirectory(actualPath, conn);
                        } else {
                            Constants.counter++;
                            hash = Utils.HashUtils.hashFile(actualPath);
                            oldHash = getOldHashIfExist(actualPath, conn, pstmt);
     
                            if (oldHash != null) {
                                if (oldHash.equals(hash)) {
                                    updateCheckedColumn(actualPath, conn, pstmt);
                                } else {
                                    Utils.ReportUtils.WriteInFile("Reference File MODIFIED : " + actualPath, Constants.REFERENCES_REPORTING_PATH);
                                    updateSignatureAndCheckedColumn(hash, actualPath, conn, pstmt);
                                }
                            } else {
                                Utils.ReportUtils.WriteInFile("Reference File ADDED : " + actualPath, Constants.REFERENCES_REPORTING_PATH);
                                insertInReferencesFilesTableDB(actualName, actualPath, hash, conn, pstmt);
                            }
                            System.out.print(".");
                            if (((Constants.counter) % 100) == 0) {
                                System.out.print("\n" + Constants.counter + "files found");
                            }
                        }
                    }
                }
            } catch (NullPointerException e) {
                System.out.println("      Problem on " + path);
                Utils.ReportUtils.WriteInFile("Problem on " + path, Constants.LOGS_PATH);
            }
        }
     
        @Override
        public void insertInReferencesFilesTableDB(String name, String url, String hash, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("INSERT INTO ReferencesFiles (Name, URL, Signature, Checked) VALUES (?,?,?,?)");
                pstmt.setString(1, name);
                pstmt.setString(2, url);
                pstmt.setString(3, hash);
                pstmt.setInt(4, 1);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("      Impossible to add " + name + " in ReferencesFiles Table !");
                Utils.ReportUtils.WriteInFile("Problem adding " + url + " file", Constants.LOGS_PATH);
            }
        }
     
        @Override
        public String getOldHashIfExist(String path, Connection conn, PreparedStatement pstmt) {
            ResultSet rs = null;
            try {
                pstmt = conn.prepareStatement("SELECT Signature FROM ReferencesFiles WHERE URL=?");
                pstmt.setString(1, path);
                rs = pstmt.executeQuery();
                if (rs.first()) {
                    String hash = rs.getString(1);
                    return hash;
                } else {
                    return null;
                }
            } catch (Exception e) {
                System.out.println("Problem checking if file of reference exist in DB (" + path + ")");
                Utils.ReportUtils.WriteInFile("Problem checking if file of reference exist in DB (" + path + ")", Constants.LOGS_PATH);
                return null;
            } finally {
                try {
                    rs.close();
                } catch (SQLException ex) {
                }
            }
        }
     
        @Override
        public void updateCheckedColumn(String url, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("UPDATE ReferencesFiles SET Checked=1 WHERE URL=?");
                pstmt.setString(1, url);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("Problem updating last checked date (" + url + ")");
            }
        }
     
        @Override
        public void updateSignatureAndCheckedColumn(String url, String newHash, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("UPDATE ReferencesFiles SET Signature=?, Checked=1 WHERE URL=?");
                pstmt.setString(1, newHash);
                pstmt.setString(2, url);
                pstmt.executeUpdate();
                pstmt.close();
            } catch (SQLException e) {
                System.out.println("Problem updating signature (" + url + ")");
            }
        }
     
        @Override
        public void checkDeletedFiles(Connection conn, PreparedStatement pstmt) {
            ResultSet rs = null;
            try {
                pstmt = conn.prepareStatement("SELECT URL FROM ReferencesFiles WHERE (Checked = 0)");
                rs = pstmt.executeQuery();
                while (rs.next()) {
                    System.out.println("!!! Reference DELETED !!! -> " + rs.getString(1));
                    deleteRecord(rs.getString(1), conn, pstmt);
                    Utils.ReportUtils.WriteInFile("Reference File DELETED " + rs.getString(1), Constants.REFERENCES_REPORTING_PATH);
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Problem checking deletedFiles");
                Utils.ReportUtils.WriteInFile("Problem checking deleted files", Constants.LOGS_PATH);
            } finally {
                try {
                    rs.close();
                } catch (SQLException ex) {
                }
            }
        }
     
        @Override
        public void deleteRecord(String path, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("DELETE FROM ReferencesFiles WHERE URL = ?");
                pstmt.setString(1, path);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("Problem deleting " + path);
                Utils.ReportUtils.WriteInFile("Problem deleting " + path, Constants.LOGS_PATH);
            }
        }
    }
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  16. #16
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Non non non c'est complètement faux ce que tu fait là : le paramètre PreparedStatement ne sert à rien puisque tu ne l'utilises pas.

    Pire tu crées un nouveau PreparedStatement dans la méthode (que tu ne libères pas en plus).


    Il suffit juste de faire ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void insertInReferencesFilesTableDB(String name, String url, String hash, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt.setString(1, name);
                pstmt.setString(2, url);
                pstmt.setString(3, hash);
                pstmt.setInt(4, 1);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("      Impossible to add " + name + " in ReferencesFiles Table !");
                Utils.ReportUtils.WriteInFile("Problem adding " + url + " file", Constants.LOGS_PATH);
            }
        }
    Le PreparedStatement étant alors créé/supprimé dans la méthode parseDirectory() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    public void parseDirectory(String path, Connection conn) {
    	final PreparedStatement insertStatement = conn.prepareStatement("INSERT INTO ReferencesFiles (Name, URL, Signature, Checked) VALUES (?,?,?,?)");
    	try {
     
    		// le code de parseDirectory() en iteratif
     
    	} finally {
    		insertStatement.close();
            }
    }


    Encore une fois je me répète mais à chaque utilisation d'une ressource (fichier, socket, Connection, PreparedStatement, ResultSet, ...) tu DOIS avoir soit un bloc try/finally pour le fermer, soit un bloc try-with-ressource si tu es sous Java 7.
    Si tu ne fais pas cela y'a déjà un problème...



    a++

  17. #17
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    D'accord mais du coup je suis quand même obliger de créer un preparedStatement pour chaque requête avant d’appeler la méthode qui fait la requête donc ça sert a rien non ?..

    Si je fais comme ca, je créer un PreparedStatement dans la methode parseDirectory, et a chaque fois que j'ai une requête à faire je passe a la méthode ce PreparedStatement, je modifie le PreparedStatement dans la méthode qui fait la requête, et une fois la méthode parseDirectory finie, je close le PreparedStatement, c'est pas bon ça ??

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    public class JdbcReferencesFilesDAO implements IReferencesFilesDAO {
     
        @Override
        public void parseDirectory(String path, Connection conn) {
            File actual = new File(path);
            String actualPath;
            String actualName;
            String hash;
            String oldHash;
            PreparedStatement pstmt = null;
     
            try {
                for (File f : actual.listFiles()) {
                    actualPath = f.getAbsolutePath();
                    actualName = f.getName();
                    if (!f.getName().equals(".snapshot")) {
                        if (f.isDirectory()) {
                            parseDirectory(actualPath, conn);
                        } else {
                            Constants.counter++;
                            hash = Utils.HashUtils.hashFile(actualPath);
                            oldHash = getOldHashIfExist(actualPath, conn, pstmt);
     
                            if (oldHash != null) {
                                if (oldHash.equals(hash)) {
                                    updateCheckedColumn(actualPath, conn, pstmt);
                                } else {
                                    Utils.ReportUtils.WriteInFile("Reference File MODIFIED : " + actualPath, Constants.REFERENCES_REPORTING_PATH);
                                    updateSignatureAndCheckedColumn(hash, actualPath, conn, pstmt);
                                }
                            } else {
                                Utils.ReportUtils.WriteInFile("Reference File ADDED : " + actualPath, Constants.REFERENCES_REPORTING_PATH);
                                insertInReferencesFilesTableDB(actualName, actualPath, hash, conn, pstmt);
                            }
                            System.out.print(".");
                            if (((Constants.counter) % 100) == 0) {
                                System.out.print("\n" + Constants.counter + "files found");
                            }
                        }
                    }
                }
                pstmt.close();
            } catch (Exception e) {
                System.out.println("      Problem on " + path);
                Utils.ReportUtils.WriteInFile("Problem on " + path, Constants.LOGS_PATH);
            }
        }
     
        @Override
        public void insertInReferencesFilesTableDB(String name, String url, String hash, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("INSERT INTO ReferencesFiles (Name, URL, Signature, Checked) VALUES (?,?,?,?)");
                pstmt.setString(1, name);
                pstmt.setString(2, url);
                pstmt.setString(3, hash);
                pstmt.setInt(4, 1);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("      Impossible to add " + name + " in ReferencesFiles Table !");
                Utils.ReportUtils.WriteInFile("Problem adding " + url + " file", Constants.LOGS_PATH);
            }
        }
     
        @Override
        public String getOldHashIfExist(String path, Connection conn, PreparedStatement pstmt) {
            ResultSet rs = null;
            try {
                pstmt = conn.prepareStatement("SELECT Signature FROM ReferencesFiles WHERE URL=?");
                pstmt.setString(1, path);
                rs = pstmt.executeQuery();
                if (rs.first()) {
                    String hash = rs.getString(1);
                    return hash;
                } else {
                    return null;
                }
            } catch (Exception e) {
                System.out.println("Problem checking if file of reference exist in DB (" + path + ")");
                Utils.ReportUtils.WriteInFile("Problem checking if file of reference exist in DB (" + path + ")", Constants.LOGS_PATH);
                return null;
            } finally {
                try {
                    rs.close();
                } catch (SQLException ex) {
                }
            }
        }
     
        @Override
        public void updateCheckedColumn(String url, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("UPDATE ReferencesFiles SET Checked=1 WHERE URL=?");
                pstmt.setString(1, url);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("Problem updating last checked date (" + url + ")");
            }
        }
     
        @Override
        public void updateSignatureAndCheckedColumn(String url, String newHash, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("UPDATE ReferencesFiles SET Signature=?, Checked=1 WHERE URL=?");
                pstmt.setString(1, newHash);
                pstmt.setString(2, url);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("Problem updating signature (" + url + ")");
            }
        }
     
        @Override
        public void checkDeletedFiles(Connection conn, PreparedStatement pstmt) {
            ResultSet rs = null;
            try {
                pstmt = conn.prepareStatement("SELECT URL FROM ReferencesFiles WHERE (Checked = 0)");
                rs = pstmt.executeQuery();
                while (rs.next()) {
                    System.out.println("!!! Reference DELETED !!! -> " + rs.getString(1));
                    deleteRecord(rs.getString(1), conn, pstmt);
                    Utils.ReportUtils.WriteInFile("Reference File DELETED " + rs.getString(1), Constants.REFERENCES_REPORTING_PATH);
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Problem checking deletedFiles");
                Utils.ReportUtils.WriteInFile("Problem checking deleted files", Constants.LOGS_PATH);
            } finally {
                try {
                    rs.close();
                } catch (SQLException ex) {
                }
            }
        }
     
        @Override
        public void deleteRecord(String path, Connection conn, PreparedStatement pstmt) {
            try {
                pstmt = conn.prepareStatement("DELETE FROM ReferencesFiles WHERE URL = ?");
                pstmt.setString(1, path);
                pstmt.executeUpdate();
            } catch (SQLException e) {
                System.out.println("Problem deleting " + path);
                Utils.ReportUtils.WriteInFile("Problem deleting " + path, Constants.LOGS_PATH);
            }
        }
    }
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  18. #18
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Quand à la gestion des ressources, tant qu'on n'est pas sous Java 7 le meilleur pattern est le suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        final ResultSet rs = pstmt.executeQuery();
        try {
            // code
        } finally {
            rs.close();
        }
    La variable doit être déclarée et initialisée en ligne (et de préférence en final), et le try/finally doit lui succéder immédiatement.
    Il ne doit pas y avoir de bloc catch(), et encore moins de catch vide !

    Si tu veux gérer les exceptions (ou ne pas les gérer comme tu le fait), alors englobe le tout dans un nouveau bloc try/catch. Ne rajoutes pas un catch() ca devient casse-gueule à gérer.


    a++

  19. #19
    Membre éclairé Avatar de Sennad
    Homme Profil pro
    Développeur Java
    Inscrit en
    Août 2014
    Messages
    180
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2014
    Messages : 180
    Points : 703
    Points
    703
    Par défaut
    Je dois avouéer que la ya des trucs que je comprend plus trop..
    Je suis bien sous java 7, mais je peux pas faire ca :
    try (ResultSet rs = pstmt.executeQuery()) {...} puisque le pstmt est défini entre les accolades, donc après..
    Sayé je suis perdu
    -----------------------------------------------------------------------------------------
    Don't play with fire if u don't wanna get burn ! Clinton - Fearon
    ____________________________________________________Pensez au

  20. #20
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    C'est à dire ? Quel est le problème précisément ?


    a++

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 5 12345 DernièreDernière

Discussions similaires

  1. comment calculer le md5 du contenu d'un fichier
    Par c.plus.plus dans le forum Débuter
    Réponses: 6
    Dernier message: 01/01/2012, 17h54
  2. Calculer le MD5/SHA-256 d'un fichier
    Par PierrotY dans le forum Sécurité
    Réponses: 26
    Dernier message: 06/04/2009, 09h35
  3. calcul et boucle sur lecture de fichier
    Par marinaetsonchat dans le forum Shell et commandes GNU
    Réponses: 1
    Dernier message: 22/11/2007, 15h15
  4. Proramme qui calcul l'espace que prend un fichier sur le HDD
    Par snoopy69 dans le forum Autres Logiciels
    Réponses: 4
    Dernier message: 26/01/2007, 17h05
  5. Calculer le MD5 d'un fichier octet par octet
    Par bouazza92 dans le forum C
    Réponses: 5
    Dernier message: 09/08/2006, 20h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo