Wie erstelle ich eine Kopie einer Datei in Android?

177

In meiner App möchte ich eine Kopie einer bestimmten Datei unter einem anderen Namen speichern (den ich vom Benutzer erhalte).

Muss ich den Inhalt der Datei wirklich öffnen und in eine andere Datei schreiben?

Was ist der beste Weg, dies zu tun?

WIE
quelle
Vor Java7 denke ich, dass die Antwort ja ist. stackoverflow.com/questions/106770/… .
Paul Grime
Überprüfen Sie ältere Post
Deepak

Antworten:

326

Um eine Datei zu kopieren und in Ihrem Zielpfad zu speichern, können Sie die folgende Methode verwenden.

public static void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

Ab API 19+ können Sie Java Automatic Resource Management verwenden:

public static void copy(File src, File dst) throws IOException {
    try (InputStream in = new FileInputStream(src)) {
        try (OutputStream out = new FileOutputStream(dst)) {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        }
    }
}
Rakshi
quelle
8
Danke dir. Nachdem ich mir den Kopf geschlagen hatte, stellte ich fest, dass das Problem darin bestand, dass ich nicht in den externen Speicher schreiben durfte. jetzt funktioniert es gut.
AS
8
@ mohitum007 Wenn die Datei nicht kopiert werden kann, wird eine Ausnahme ausgelöst. Verwenden Sie beim Aufrufen der Methode einen try catch-Block.
Nima G
9
Wenn eine Ausnahme ausgelöst wird, werden die Streams erst geschlossen, wenn der Müll gesammelt wurde, und das ist nicht gut . Erwägen Sie, sie zu schließen finally.
Pang
1
@Stich. Du hast recht. Was noch schlimmer ist, in / out.close () muss aufgerufen werden, sonst tritt im zugrunde liegenden Betriebssystem ein Ressourcenverlust auf, da der GC die offenen Dateideskriptoren niemals schließt. GC kann keine Betriebssystemressourcen außerhalb der JVM wie Dateideskriptoren oder Sockets schließen. Diese müssen immer programmgesteuert in einer finally-Klausel geschlossen werden oder die neue Syntax try-with-resource verwenden: docs.oracle.com/javase/tutorial/essential/exceptions/…
earizon
4
Bitte schließen Sie beide Streams innerhalb eines Endes. Wenn es eine Ausnahme gibt, wird Ihr Stream-Speicher nicht erfasst.
Pozuelog
121

Alternativ können Sie FileChannel verwenden, um eine Datei zu kopieren. Es ist möglicherweise schneller als die Byte-Kopiermethode beim Kopieren einer großen Datei. Sie können es jedoch nicht verwenden, wenn Ihre Datei größer als 2 GB ist.

public void copy(File src, File dst) throws IOException {
    FileInputStream inStream = new FileInputStream(src);
    FileOutputStream outStream = new FileOutputStream(dst);
    FileChannel inChannel = inStream.getChannel();
    FileChannel outChannel = outStream.getChannel();
    inChannel.transferTo(0, inChannel.size(), outChannel);
    inStream.close();
    outStream.close();
}
NullNoname
quelle
3
transferTo kann eine Ausnahme auslösen. In diesem Fall lassen Sie Streams offen. Genau wie Pang und Nima in akzeptierter Antwort kommentierten.
Viktor Brešan
Außerdem sollte transferTo innerhalb der Schleife aufgerufen werden, da dies nicht garantiert, dass der angeforderte Gesamtbetrag übertragen wird.
Viktor Brešan
Ich habe Ihre Lösung ausprobiert und sie schlägt für mich mit Ausnahme java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)des FileOutputStream outStream = new FileOutputStream(dst);Schrittes fehl . Laut dem Text ist mir klar, dass die Datei nicht existiert, also überprüfe ich sie und rufe dst.mkdir();bei Bedarf auf, aber es hilft immer noch nicht. Ich habe auch versucht zu überprüfen dst.canWrite();und es kehrte zurück false. Darf dies die Ursache des Problems sein? Und ja, das habe ich <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Mike B.
1
@ ViktorBrešan Nach API 19 können Sie die automatische Ressourcenverwaltung von Java verwenden, indem Sie die try ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
In-
Gibt es eine Möglichkeit, diese Lösung dazu zu bringen, ihren Fortschritt zu veröffentlichen onProgressUpdate, damit ich sie in einer ProgressBar anzeigen kann? In der akzeptierten Lösung kann ich den Fortschritt in der while-Schleife berechnen, aber ich kann hier nicht sehen, wie es geht.
George Bezerra
30

Kotlin Erweiterung dafür

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}
Dima Rostopira
quelle
Dies ist die präziseste und zugleich flexibelste Antwort. Einfachere Antworten berücksichtigen keine URIs, die über geöffnet wurden contentResolver.openInputStream(uri).
AjahnCharles
6
Kotlin ist Liebe, Kotlin ist Leben!
Dev Aggarwal
17

Diese haben gut für mich funktioniert

public static void copyFileOrDirectory(String srcDir, String dstDir) {

    try {
        File src = new File(srcDir);
        File dst = new File(dstDir, src.getName());

        if (src.isDirectory()) {

            String files[] = src.list();
            int filesLength = files.length;
            for (int i = 0; i < filesLength; i++) {
                String src1 = (new File(src, files[i]).getPath());
                String dst1 = dst.getPath();
                copyFileOrDirectory(src1, dst1);

            }
        } else {
            copyFile(src, dst);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if (!destFile.getParentFile().exists())
        destFile.getParentFile().mkdirs();

    if (!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    } finally {
        if (source != null) {
            source.close();
        }
        if (destination != null) {
            destination.close();
        }
    }
}
Bojan Kseneman
quelle
13

Dies ist unter Android O (API 26) einfach. Wie Sie sehen:

  @RequiresApi(api = Build.VERSION_CODES.O)
  public static void copy(File origin, File dest) throws IOException {
    Files.copy(origin.toPath(), dest.toPath());
  }
LeeR
quelle
10

Für eine Antwort ist es möglicherweise zu spät, aber der bequemste Weg ist die Verwendung

FileUtils's

static void copyFile(File srcFile, File destFile)

zB habe ich das getan

`

private String copy(String original, int copyNumber){
    String copy_path = path + "_copy" + copyNumber;
        try {
            FileUtils.copyFile(new File(path), new File(copy_path));
            return copy_path;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

`

stopBugs
quelle
20
FileUtils ist in Android nicht nativ vorhanden.
Die Berga
2
Aber es gibt eine Bibliothek von Apache, die dies und mehr tut
Alessandro Muzzi
7

Mit Kotlin jetzt viel einfacher:

 File("originalFileDir", "originalFile.name")
            .copyTo(File("newFileDir", "newFile.name"), true)

trueoder falsedient zum Überschreiben der Zieldatei

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html

Blundell
quelle
Nicht so einfach, wenn Ihre Datei aus einer Uri-Galerie stammt.
AjahnCharles
Lesen Sie die Kommentare unter dieser Antwort: "Diese Antwort ist aktiv schädlich und verdient nicht die Stimmen, die sie erhält. Sie schlägt fehl, wenn der Uri ein Inhalt ist: // oder ein anderer Nicht-Datei-Uri." (Ich habe positiv gestimmt - ich wollte nur klarstellen, dass es keine Silberkugel ist)
AjahnCharles
5

Hier ist eine Lösung, die die Eingabe- / Ausgabestreams tatsächlich schließt, wenn beim Kopieren ein Fehler auftritt. Diese Lösung verwendet Apache Commons IO IOUtils-Methoden zum Kopieren und Behandeln des Schließens von Streams.

    public void copyFile(File src, File dst)  {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(src);
            out = new FileOutputStream(dst);
            IOUtils.copy(in, out);
        } catch (IOException ioe) {
            Log.e(LOGTAG, "IOException occurred.", ioe);
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
        }
    }
SBerg413
quelle
Es sieht einfach aus
Serg Burlaka
2

in Kotlin nur:

val fileSrc : File = File("srcPath")
val fileDest : File = File("destPath")

fileSrc.copyTo(fileDest)
Sodino
quelle