On nous demande de réaliser une copie de fichier, à l'aide de deux paramètres passés dans la console, lors du lancement du programme. Une grande partie du code de ce chapitre, peut être réutilisée : on s'inspirera des codes d'utilisation des flux d'octets et des codes d'utilisation des tampons.
Cet exercice ne présente pas de difficulté majeure. Mais tout de même quelques remarques par rapport au code que je vais vous soumettre :
- j'ai pris soin de vérifier que l'on a bien deux arguments dans la ligne de commandes ;
- voici un exemple d'erreur de type FileNotFoundException :
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:137)
at java.io.FileInputStream.<init>(FileInputStream.java:96)
at CopieFichier.copier(CopieFichier.java:182)
at CopieFichier.main(CopieFichier.java:171)
Le message de l'exception est alors ce qui se situe en première ligne, juste après les deux points.
Comme le premier contenu du message est le nom du fichier que l'on a tenté d'ouvrir, on va tenter de l'extraire. En appelant la méthode split(" ") - la chaine en paramètre se constitue d'un seul espace - sur une chaine de caractères, on coupe la chaine selon ses espaces.
String s = "Ceci est, un exemple, un simple exemple.";
String [] morceaux = s.split(" ");
On obtient donc le tableau morceaux suivant :
{" Ceci ", " est, ", " un ", " exemple, ", " un ", " simple ", " exemple. "}
Ainsi pour extraire le nom du fichier, on appellera :
exception.getMessage().split[0];
- j'ai préféré construire une méthode qui gère la copie, plutôt que de coder le tout en dur dans le main() ;
- enfin on referme, dans la clause finally, les différents flux. On prend soin de fermer les flux de lecture avant les flux d'écriture (et un tampon avant le flux auquel il est relié).
Voici donc le code que j'ai à vous proposer :
Copie de fichier (mode console)
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.swing.JOptionPane;
public class CopieFichierModeConsole {
public static void main(String[] args) {
if (args.length != 2){
System.err.println("Utilisation : CopierFicher source destination");
System.exit(1);
}
CopieFichierModeConsole copieurFichier = new CopieFichierModeConsole();
copieurFichier.copier(args[0], args[1]);
}
public void copier(String source, String destination){
FileInputStream fichierLecture = null;
FileOutputStream fichierSauvegarde = null;
BufferedInputStream tamponFichierLecture = null;
BufferedOutputStream tamponFichierSauvegarde = null;
try {
fichierLecture = new FileInputStream(source);
tamponFichierLecture = new BufferedInputStream(fichierLecture);
fichierSauvegarde = new FileOutputStream(destination);
tamponFichierSauvegarde = new BufferedOutputStream(fichierSauvegarde);
while(true){
int valeurEntiereOctet = tamponFichierLecture.read();
if (valeurEntiereOctet == -1)
break;
tamponFichierSauvegarde.write(valeurEntiereOctet);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
String [] motsDuMessage = e.getMessage().split(" ");
String message = "Impossible d'ouvrir le fichier "+motsDuMessage[0]+" ."
+"\nVerifiez que le fichier existe et qu'il ne s'agisse pas" +
" du tout d'un repertoire.";
JOptionPane.showMessageDialog(null,message,
"Erreur d'ouverture de fichier", JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null,
"Un erreur de lecture ou d'ecriture diverse est survenue.",
"Erreur diverse", JOptionPane.ERROR_MESSAGE);
}
finally{
try {
tamponFichierSauvegarde.flush();
tamponFichierLecture.close();
fichierLecture.close();
tamponFichierSauvegarde.close();
fichierSauvegarde.close();
}
catch (IOException exception){
exception.printStackTrace();
}
}
}
}
VIII-B-1. Utilisation du composant Swing JFileChooser▲
Ce composant permet d'afficher une boîte de dialogue Ouvrir/EnregisterSous, nous permettant donc de choisir un fichier grâce à une fenêtre conviviale.
Tout d'abord, il faut créer une instance de JFileChooser. On peut ensuite se contenter d'appeler :
- sa méthode showOpenDialog pour afficher une fenêtre de sélection de fichier pour une ouverture ;
- sa méthode showSaveDialog pour afficher une fenêtre de sélection de fichier pour une sauvegarde.
Dans les deux cas, on obtient un int en retour, nous indiquant de quelle manière l'utilisateur a quitté la fenêtre. S'il a validé sa sélection, on obtient la valeur JFileChooser.APPROVE_OPTION.
Ceci fait, on dispose de plusieurs méthodes nous renseignant sur la dernière sélection utilisateur, dont les plus utiles ici seront :
- getSelectedFile() : on obtient un objet de type File, correspondant au fichier sélectionné ;
- getCurrentDirectory() : on obtient le répertoire du fichier sélectionné.
Enfin, le constructeur
JFileChooser(File currentDirectory)
permet de choisir le répertoire affiché lors du lancement du menu : une valeur de null laissera le menu afficher le répertoire par défaut.
VIII-B-2. L'utilisation de classe locale et des constantes▲
Vous connaissiez les variables locales : eh bien sachez maintenant que vous pouvez aussi déclarer des classes localement à une méthode. Bien sûr les inconvénients principaux d'une telle pratique :
- la classe ne sera pas réutilisable en dehors de la méthode où elle a été déclarée ;
- cela ne va pas en améliorant la clarté, la lisibilité du code.
Alors pourquoi utiliser de telles classes ? J'ai choisi de déclarer localement deux classes implémentant l'interface ActionListener dans le constructeur, parce que de cette manière les variables locales au constructeur peuvent être directement réutilisées dans la classe interne. Seule condition : que l'on déclare les variables réutilisées comme des constantes, grâce au mot-clé final.
final int monEntierConstant = 10 ;
Une constante est une variable qui ne pourra jamais être modifiée, sous peine d'obtenir une erreur de compilation. Les constantes peuvent aussi être utiles pour éviter de coder les variables en dur. Ainsi au lieu d'écrire, pour redimensionner une fenêtre :
Vous pouvez écrire :
final int tailleEnX = 10 ;
final int tailleEnY = 20 ;
fenetre.setSize(tailleEnX, tailleEnY) ;
Voici un exemple de déclaration/utilisation d'une classe interne dans une méthode :
Exemple de classe interne locale à une méthode
Sélectionnez
void construireFenetre(){
final String texte1 = "MonTexte1" ;
final String texte2 = "AutreTexte2" ;
final JButton monBouton = new JButton(texte1) ;
class MonActionPourBouton implements ActionListener {
public void actionPerformed(ActionEvent evt){
if (texte1.equals(monBouton.getText()){
monBouton.setText(texte2) ;
}
else if (texte2.equals(monBouton.getText()){
monBouton.setText(texte1) ;
}
}
};
}
monBouton.addActionListener(new MonActionPourBouton()) ;
Ici le texte du bouton basculera entre le contenu de texte1 et le contenu de texte2 à chaque clic.
VIII-B-3. Dernières considérations▲
J'ai réutilisé la méthode copier() de l'exercice précédent presque telle quelle : dans chaque close catch, je n'affiche plus mon message d'erreur personnalisé, mais je me contente de renvoyer une exception du même type en remplaçant le message par le mien.
La classe CopieFichier est maintenant une classe fille de la classe JFrame.
VIII-B-4. Le code complet▲
Copie de fichier (version graphique)
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class CopieFichierModeGraphique extends JFrame {
File fichierSource = null, fichierDestination = null;
File repertoireSource = null, repertoireDestination = null;
public CopieFichierModeGraphique(){
setTitle("Copie de fichiers");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(4, 3));
add(new JLabel("Copier depuis : "));
final JTextField texteSource = new JTextField(30);
texteSource.setEditable(false);
add(texteSource);
JButton boutonSource = new JButton("Parcourir ...");
class ActionBoutonSource implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
JFileChooser fenetreChoixFichier = new JFileChooser(repertoireSource);
int modeFermeture = fenetreChoixFichier.showOpenDialog(CopieFichierModeGraphique.this);
if (modeFermeture == JFileChooser.APPROVE_OPTION){
repertoireSource = fenetreChoixFichier.getCurrentDirectory();
fichierSource = fenetreChoixFichier.getSelectedFile();
texteSource.setText(fichierSource.getAbsolutePath());
}
}
};
boutonSource.addActionListener(new ActionBoutonSource());
add(boutonSource);
add(new JLabel("Copier vers : "));
final JTextField texteDestination = new JTextField(30);
texteDestination.setEditable(false);
add(texteDestination);
JButton boutonDestination = new JButton("Parcourir ...");
class ActionBoutonDestination implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
JFileChooser fenetreChoixFichier = new JFileChooser(repertoireDestination);
int modeFermeture = fenetreChoixFichier.showSaveDialog(CopieFichierModeGraphique.this);
if (modeFermeture == JFileChooser.APPROVE_OPTION){
repertoireDestination = fenetreChoixFichier.getCurrentDirectory();
fichierDestination = fenetreChoixFichier.getSelectedFile();
texteDestination.setText(fichierDestination.getAbsolutePath());
}
}
};
boutonDestination.addActionListener(new ActionBoutonDestination());
add(boutonDestination);
add(new JLabel());
JButton boutonCopier = new JButton("Copier");
class ActionBoutonCopier implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
if (fichierSource == null){
JOptionPane.showMessageDialog(CopieFichierModeGraphique.this,
"Veuillez selectionner un fichier source.",
"Selection incomplete",
JOptionPane.ERROR_MESSAGE
);
return;
}
if (fichierDestination == null){
JOptionPane.showMessageDialog(CopieFichierModeGraphique.this,
"Veuillez selectionner un fichier destination.",
"Selection incomplete",
JOptionPane.ERROR_MESSAGE
);
return;
}
try {
copier(fichierSource.getAbsolutePath(),
fichierDestination.getAbsolutePath());
JOptionPane.showMessageDialog(CopieFichierModeGraphique.this, "Le fichier a ete copie avec success.");
} catch (FileNotFoundException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(CopieFichierModeGraphique.this, e.getMessage(),
"Fichier non trouve", JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(CopieFichierModeGraphique.this, e.getMessage(),
"Erreur de lecture/ecriture diverse", JOptionPane.ERROR_MESSAGE);
}
}
}
boutonCopier.addActionListener(new ActionBoutonCopier());
add(boutonCopier);
add(new JLabel());
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
new CopieFichierModeGraphique().setVisible(true);
}
public void copier(String source, String destination)
throws FileNotFoundException, IOException {
FileInputStream fichierLecture = null;
FileOutputStream fichierSauvegarde = null;
BufferedInputStream tamponFichierLecture = null;
BufferedOutputStream tamponFichierSauvegarde = null;
try {
fichierLecture = new FileInputStream(source);
tamponFichierLecture = new BufferedInputStream(fichierLecture);
fichierSauvegarde = new FileOutputStream(destination);
tamponFichierSauvegarde = new BufferedOutputStream(fichierSauvegarde);
while(true){
int valeurEntiereOctet = tamponFichierLecture.read();
if (valeurEntiereOctet == -1)
break;
tamponFichierSauvegarde.write(valeurEntiereOctet);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
String [] motsDuMessage = e.getMessage().split(" ");
String message = "Impossible d'ouvrir le fichier "+motsDuMessage[0]+" ."
+"\nVerifiez que le fichier existe et qu'il ne s'agisse pas" +
" du tout d'un repertoire.";
throw new FileNotFoundException(message);
} catch (IOException e) {
e.printStackTrace();
String message = "Un erreur de lecture ou d'ecriture diverse est survenue.";
throw new IOException(message);
}
finally{
try {
tamponFichierSauvegarde.flush();
tamponFichierLecture.close();
fichierLecture.close();
tamponFichierSauvegarde.close();
fichierSauvegarde.close();
}
catch (IOException exception){
exception.printStackTrace();
}
}
}
}