Wie kann eine Datei kopiert werden?

Beim Kopieren einer Datei müssen ggfs. sowohl der Dateityp, als auch der Speicherort beachtet werden.

Im Folgenden werden vier Beispiele gezeigt, die jeweils in einer statischen Methode gekapselt sind. Der jeweilige Methodenaufruf kann direkt in main() erfolgen, sodass die ausführende Klasse wie folgt aussehen kann.

public class DateiKopierenBsp {

    private static final String IN_PATH = "/<pfadZu>/<datei>";
    private static final String OUT_PATH = "/<pfadZu>/<kopierteDatei>";
    
    public static void main(String[] args) {
        // hier Methodenaufruf
    }
    
    //...
}

Kopieren innerhalb des lokalen Dateibaums

Sollen beliebige Dateitypen innerhalb des lokalen Dateibaums von einem Ort zum anderen kopiert werden, so kann dies mittels der Klasse FileChannel erfolgen.

public static boolean copyFileFromExtToExtByChannel() {
    File in = new File(IN_PATH);
    File out = new File(OUT_PATH);
    try (FileInputStream fis = new FileInputStream(in);
         FileOutputStream fos = new FileOutputStream(out);
         FileChannel inChannel = fis.getChannel();
         FileChannel outChannel = fos.getChannel();) {
         inChannel.transferTo(0, inChannel.size(), outChannel);
    } catch (IOException e) {
        return false;
    }
    return true;
}

Hierzu müssen die beiden Dateien als File-Objekte vorliegen. Sie werden im Beispiel anhand des jeweiligen Pfades erzeugt und an die Konstruktoren von FileInputStream bzw. FileOutputStream übergeben. Von beiden werden FileChannel-Objekte gebildet. Sowohl die Streams, als auch die Channel müssen nach ihrer Nutzung wieder geschlossen werden. Um dies zu automatisieren, werden deren Deklarationen in ein Try-with-Resources-Statement eingeschlossen [4].
Die Klasse FileChannel kennt die Methode transferTo(), die auf dem Eingabe-Channel aufgerufen wird. Ihr werden drei Parameter übergeben, zwei long-Werte und der Ausgabe-Channel. Der erste long-Wert bezeichnet den Startpunkt des Dateitransfers innerhalb der Datei (bei einer Kopie natürlich das erste byte), der zweite die Länge des Transfers in bytes.
Die Methode kann auch verwendet werden, um Dateien aus dem Classpath heraus zu exportieren. Hierzu muss der Eingabepfad, etwa innerhalb eines Eclipse-Projektes, dann in der Form

src<package><dateiname> // ohne führenden '/'!

angegeben werden. Vorsicht ist jedoch geboten, wenn das Projekt als *.jar gepackt werden soll. Der Export aus dem Archiv heraus ist zur Laufzeit auf diese Weise nicht möglich.

Ohne den Komfort eines FileChannel, der weitere Funktionen wie Locking, partielles Kopieren, etc. bereitstellt, kann ein solcher Transfer auch byte für byte direkt durch die Streams erfolgen. Hierbei wird der Dateiinhalt, unter Auslassung der Channel, in einem byte-Array zwischengespeichert und dann innerhalb einer Schleife durch den OutputStream in eine neuen Datei wieder eingelesen.

public static boolean copyFileFromExtToExt() {
    try (FileInputStream fis = new FileInputStream(IN_PATH);
         FileOutputStream fos = new FileOutputStream(OUT_PATH);) {
         byte[] buf = new byte[1024];
         int i = 0;
         while ((i = fis.read(buf)) != -1) {
             fos.write(buf, 0, i);
         }
    } catch (IOException e) {
        return false;
    }
    return true;
}

Laut eigenen, nicht allgemeingültigen Messungen ist der Transfer durch FileChannel ca. 4-5 mal schneller.

Kopieren aus dem ClassPath in den lokalen Dateibaum

Ersetzt man den FileInputStream durch einen InputStream, so kann man sich dieses Verfahren zunutze machen, um Dateien zur Laufzeit aus dem *.jar-Archiv der Applikation heraus in den lokalen Dateibaum zu kopieren. Die Ursache liegt darin, dass Java in *.jar-Archiven gespeicherte Dateien nicht als solche behandelt.

public static boolean copyBinaryFileFromClassPathToExt() {
    try (InputStream is = DateiKopierenBsp.class.getResourceAsStream(IN_PATH);
         FileOutputStream fos = new FileOutputStream(OUT_PATH);) {
         byte[] buf = new byte[1024];
         int i = 0;
         while((i=is.read(buf))!=-1) 
         {
            fos.write(buf, 0, i);
         }
    } catch (IOException e) {
        return false;
    }
    return true;
}

Desweiteren sollten die Daten dabei durch getResourceAsStream() geladen werden. Die Methode ist in der Klasse Class definiert, sodass zunächst das Class-Objekt der ladenden Klasse ermittelt werden muss. Wie hier, in statischen Umgebungen, muss dies durch die Eigenschaft class, bei Objekten duch getClass() erfolgen.

Bei textbasierten Dateien kann die Kopie auch mit Hilfe eines ByteArrayOutputStream und eines PrintWriter erfolgen. Das Einlesen der Daten bleibt dabei unverändert. Dafür kann man sich die Vorteile des ByteArrayOutputStream zunutze machen und das gewünschte Character-Set festlegen [9].

public static boolean copyTextFileFromClassPathToExt() {
    try (InputStream is = DateiKopierenBsp.class.getResourceAsStream(IN_PATH);
         PrintWriter pWriter = new PrintWriter(new FileWriter(OUT_PATH));) {
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
         for (int length; (length = is.read(buffer)) != -1;) {
            bos.write(buffer, 0, length);
         }
         String s = bos.toString("UTF-8");
         pWriter.println(s);
    } catch (IOException e) {
         return false;
    }
    return true;
}

Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.