Làm thế nào để tạo một bản sao của một tập tin trong Android?


178

Trong ứng dụng của mình, tôi muốn lưu một bản sao của một tệp nhất định có tên khác (mà tôi nhận được từ người dùng)

Tôi có thực sự cần phải mở nội dung của tập tin và ghi nó vào tập tin khác không?

Cách tốt nhất để làm như vậy là gì?


Trước Java7, tôi nghĩ câu trả lời là có bạn làm. stackoverflow.com/questions/106770/ đá .
Paul Grime


kiểm tra bài
Deepak

Câu trả lời:


327

Để sao chép một tập tin và lưu nó vào đường dẫn đích của bạn, bạn có thể sử dụng phương pháp bên dưới.

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

Trên API 19+, bạn có thể sử dụng Quản lý tài nguyên tự động Java:

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

8
Cảm ơn bạn. Sau khi đập đầu tôi, tôi thấy vấn đề là không được phép ghi vào bộ nhớ ngoài. bây giờ nó hoạt động tốt
NHƯ

8
@ mohitum007 nếu tệp không sao chép thì sẽ ném ngoại lệ. sử dụng một khối thử bắt khi gọi phương thức.
Nima G

9
Nếu một ngoại lệ được ném ra, các luồng sẽ không bị đóng cho đến khi chúng được thu gom rác và điều đó không tốt . Xem xét đóng chúng vào finally.
Pang

1
@Pang. Bạn đúng rồi. Điều tồi tệ hơn là in / out.close () phải được gọi hoặc nếu không sẽ có rò rỉ tài nguyên trong HĐH cơ bản, vì GC sẽ không bao giờ đóng bộ mô tả tệp đang mở. GC không thể đóng tài nguyên hệ điều hành bên ngoài JVM như mô tả tệp hoặc ổ cắm. Chúng phải luôn được đóng trong một mệnh đề cuối cùng theo chương trình hoặc sử dụng cú pháp mới try-with-resource: docs.oracle.com/javase/tutorial/essential/exceptions/ Lỗi
Earizon 18/07/2015

4
Vui lòng, đóng cả hai luồng bên trong cuối cùng, nếu có Excepcion, bộ nhớ luồng của bạn sẽ không được thu thập.
pozuelog

121

Ngoài ra, bạn có thể sử dụng FileChannel để sao chép tệp. Nó thể nhanh hơn phương thức sao chép byte khi sao chép một tệp lớn. Bạn không thể sử dụng nó nếu tập tin của bạn lớn hơn 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();
}

3
transferTo có thể đưa ra một ngoại lệ và trong trường hợp đó bạn sẽ để các luồng mở. Giống như Pang và Nima nhận xét trong câu trả lời được chấp nhận.
Viktor Brešan

Ngoài ra, transferTo nên được gọi bên trong vòng lặp vì nó không đảm bảo rằng nó sẽ chuyển tổng số tiền được yêu cầu.
Viktor Brešan

Tôi đã thử giải pháp của bạn và nó thất bại với tôi ngoại lệ java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)FileOutputStream outStream = new FileOutputStream(dst);bước này. Theo văn bản tôi nhận ra, tập tin không tồn tại, vì vậy tôi kiểm tra nó và gọi dst.mkdir();nếu cần, nhưng nó vẫn không giúp được gì. Tôi cũng đã thử kiểm tra dst.canWrite();và nó trở lại false. Đây có thể là nguồn gốc của vấn đề? Và vâng, tôi có <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Mike B.

1
@ ViktorBrešan Sau API 19, bạn có thể sử dụng quản lý tài nguyên tự động Java bằng cách xác định luồng vào và ra khi mở thửtry ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
AlbertMarkovski

Có cách nào để làm cho giải pháp này xuất bản tiến trình của nó lên onProgressUpdate, vì vậy tôi có thể hiển thị nó trong ProgressBar không? Trong giải pháp được chấp nhận, tôi có thể tính toán tiến trình trong vòng lặp while, nhưng tôi không thể thấy cách thực hiện ở đây.
George Bezerra

31

Phần mở rộng của Kotlin cho nó

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}

Đây là câu trả lời ngắn gọn nhưng linh hoạt nhất. Câu trả lời đơn giản hơn không thể giải thích cho các URI được mở thông qua contentResolver.openInputStream(uri).
AjahnCharles

6
Kotlin là tình yêu, Kotlin là cuộc sống!
Dev Aggarwal

17

Chúng làm việc tốt cho tôi

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

13

Điều này đơn giản trên Android O (API 26), như bạn thấy:

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

10

Có thể đã quá muộn để trả lời nhưng cách thuận tiện nhất là sử dụng

FileUtils'S

static void copyFile(File srcFile, File destFile)

ví dụ như đây là những gì tôi đã làm

`

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

`


20
FileUtils không tồn tại tự nhiên trong Android.
Berga

2
Nhưng có một thư viện được tạo bởi Apache để thực hiện điều này và hơn thế nữa: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/ phỏng
Alessandro Muzzi

7

Bây giờ đơn giản hơn nhiều với Kotlin:

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

truehoặc falselà để ghi đè tập tin đích

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


Không đơn giản như vậy nếu Tệp của bạn đến từ Thư viện có ý định Uri.
AjahnCharles

Đọc các bình luận bên dưới câu trả lời: "Câu trả lời này có hại tích cực và không xứng đáng với số phiếu mà nó nhận được. Không thành công nếu Uri là một nội dung: // hoặc bất kỳ Uri không có tệp nào khác." (Tôi đã upvote - Tôi chỉ muốn làm rõ nó không phải là viên đạn bạc)
AjahnCharles

5

Đây là một giải pháp thực sự đóng các luồng đầu vào / đầu ra nếu xảy ra lỗi trong khi sao chép. Giải pháp này sử dụng các phương thức apache Commons IO IOUtils cho cả sao chép và xử lý việc đóng luồng.

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

Trông có vẻ đơn giản
Serg Burlaka

2

trong kotlin, chỉ:

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

fileSrc.copyTo(fileDest)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.