Bagaimana cara membuat salinan file di android?

178

Di aplikasi saya, saya ingin menyimpan salinan file tertentu dengan nama yang berbeda (yang saya dapatkan dari pengguna)

Apakah saya benar-benar perlu membuka konten file dan menulisnya ke file lain?

Apa cara terbaik untuk melakukannya?

SEBAGAI
sumber
Pra Java7, saya pikir jawabannya adalah ya Anda lakukan. stackoverflow.com/questions/106770/… .
Paul Grime
periksa pos
Deepak

Jawaban:

327

Untuk menyalin file dan menyimpannya ke jalur tujuan Anda, Anda dapat menggunakan metode di bawah ini.

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();
    }
}

Pada API 19+ Anda dapat menggunakan Java Automatic Resource Management:

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
sumber
8
Terima kasih. setelah membenturkan kepalaku, saya menemukan masalah adalah tidak adanya izin untuk menulis ke penyimpanan eksternal. sekarang berfungsi dengan baik.
AS
8
@ mohitum007 jika file gagal disalin maka pengecualian dilemparkan. gunakan try catch block saat memanggil metode.
Nima G
9
Jika pengecualian dilemparkan, sungai tidak akan ditutup sampai mereka mengumpulkan sampah, dan itu tidak baik . Pertimbangkan untuk menutupnya finally.
Pang
1
@Pang. Kamu benar. Yang lebih buruk, in / out.close () harus dipanggil atau jika tidak akan ada kebocoran sumber daya di OS yang mendasarinya, karena GC tidak akan pernah menutup deskriptor file yang terbuka. GC tidak dapat menutup sumber daya OS di luar JVM seperti deskriptor file atau soket. Itu harus selalu ditutup pada klausa akhirnya secara terprogram atau menggunakan sintaks baru coba-dengan-sumber daya: docs.oracle.com/javase/tutorial/essential/exceptions/…
earizon
4
Tolong, tutup kedua Streaming di dalam akhirnya, jika ada Excepcion, memori stream Anda tidak akan dikumpulkan.
pozuelog
121

Atau, Anda dapat menggunakan FileChannel untuk menyalin file. Ini mungkin lebih cepat daripada metode copy byte saat menyalin file besar. Anda tidak dapat menggunakannya jika file Anda lebih besar dari 2GB.

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
sumber
3
transferTo dapat melempar pengecualian dan dalam hal ini Anda membiarkan aliran terbuka. Sama seperti Pang dan Nima berkomentar dalam jawaban yang diterima.
Viktor Brešan
Juga, transferTo harus dipanggil di dalam loop karena tidak menjamin bahwa itu akan mentransfer jumlah total yang diminta.
Viktor Brešan
Saya mencoba solusi Anda dan gagal bagi saya dengan pengecualian java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)di FileOutputStream outStream = new FileOutputStream(dst);langkah. Menurut teks yang saya sadari, bahwa file itu tidak ada, jadi saya memeriksanya dan menelepon dst.mkdir();jika perlu, tetapi masih tidak membantu. Saya juga mencoba memeriksa dst.canWrite();dan mengembalikannya false. Bisakah ini sumber masalahnya? Dan ya, sudah <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Mike B.
1
@ ViktorBrešan Setelah API 19 Anda dapat menggunakan manajemen sumber daya otomatis Java dengan mendefinisikan aliran masuk dan keluar dalam pembukaan percobaan Andatry ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
AlbertMarkovski
Apakah ada cara untuk membuat solusi ini mempublikasikan kemajuannya onProgressUpdate, sehingga saya bisa menunjukkannya di ProgressBar? Dalam solusi yang diterima saya dapat menghitung kemajuan dalam loop sementara, tetapi saya tidak bisa melihat bagaimana melakukannya di sini.
George Bezerra
31

Ekstensi Kotlin untuk itu

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}
Dima Rostopira
sumber
Ini adalah jawaban yang paling ringkas namun fleksibel. Jawaban yang lebih sederhana gagal menjelaskan URI yang dibuka melalui contentResolver.openInputStream(uri).
AjahnCharles
6
Kotlin adalah cinta, Kotlin adalah hidup!
Dev Aggarwal
17

Ini bekerja baik untuk saya

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
sumber
13

Ini sederhana di Android O (API 26), Seperti yang Anda lihat:

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

Mungkin sudah terlambat untuk jawaban tetapi cara yang paling nyaman adalah menggunakan

FileUtilsini

static void copyFile(File srcFile, File destFile)

misalnya ini yang saya lakukan

`

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
sumber
20
FileUtils tidak ada secara asli di Android.
The Berga
2
Tetapi ada perpustakaan yang dibuat oleh Apache yang melakukan ini dan lebih banyak lagi: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/…
Alessandro Muzzi
7

Lebih sederhana sekarang dengan Kotlin:

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

trueatau falseuntuk menimpa file tujuan

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

Blundell
sumber
Tidak sesederhana itu jika File Anda berasal dari Galeri maksud Uri.
AjahnCharles
Baca komentar di bawah jawaban itu: "Jawaban ini berbahaya dan tidak layak mendapat suara. Itu gagal jika Uri adalah konten: // atau Uri non-file lainnya." (Saya benar-benar memilih - saya hanya ingin mengklarifikasi bahwa itu bukan peluru perak)
AjahnCharles
5

Berikut ini adalah solusi yang benar-benar menutup aliran input / output jika terjadi kesalahan saat menyalin. Solusi ini menggunakan metode apache Commons IO IOUtils untuk menyalin dan menangani penutupan aliran.

    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
sumber
Itu terlihat sederhana
Serg Burlaka
2

di kota, hanya:

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

fileSrc.copyTo(fileDest)
Sodino
sumber