จะทำสำเนาไฟล์ใน Android ได้อย่างไร?


178

ในแอพของฉันฉันต้องการบันทึกสำเนาของไฟล์ที่มีชื่ออื่น (ซึ่งฉันได้รับจากผู้ใช้)

ฉันจำเป็นต้องเปิดเนื้อหาของไฟล์และเขียนลงในไฟล์อื่นหรือไม่?

วิธีที่ดีที่สุดที่จะทำคืออะไร?


ก่อน Java7 ฉันคิดว่าคำตอบคือใช่คุณ stackoverflow.com/questions/106770/… .
Paul Grime


ตรวจสอบโพสต์
Deepak

คำตอบ:


327

ในการคัดลอกไฟล์และบันทึกไว้ในเส้นทางปลายทางของคุณคุณสามารถใช้วิธีการด้านล่าง

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

บน API 19+ คุณสามารถใช้ 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);
            }
        }
    }
}

8
ขอบคุณ. หลังจากต่อสู้กับหัวฉันพบว่าปัญหาขาดการอนุญาตให้เขียนไปยังที่จัดเก็บข้อมูลภายนอก ตอนนี้มันใช้งานได้ดี
AS

8
@ mohitum007 หากไฟล์ไม่สามารถคัดลอกได้จะมีข้อผิดพลาดเกิดขึ้น ใช้ลอง catch catch block เมื่อเรียกใช้เมธอด
Nima G

9
หากยกเว้นจะโยนลำธารจะไม่ถูกปิดจนกว่าพวกเขาจะเก็บรวบรวมขยะกำลังและที่ไม่ดี finallyพิจารณาปิดพวกเขาใน
ปาง

1
@Pang คุณพูดถูก สิ่งที่แย่กว่านั้นคือใน / out.close () จะต้องถูกเรียกมิฉะนั้นจะมีการรั่วไหลของทรัพยากรในระบบปฏิบัติการพื้นฐานเนื่องจาก GC จะไม่ปิดตัวอธิบายไฟล์ที่เปิดอยู่ GC ไม่สามารถปิดทรัพยากร OS ภายนอก JVM ได้เช่นตัวอธิบายไฟล์หรือซ็อกเก็ต สิ่งเหล่านั้นจะต้องถูกปิดในประโยคสุดท้ายโดยทางโปรแกรมหรือใช้ไวยากรณ์แบบลองกับทรัพยากร: docs.oracle.com/javase/tutorial/essential/exceptions/…
earizon

4
ได้โปรดปิด Streams ทั้งสองข้างในที่สุดหากมี Excepcion หน่วยความจำสตรีมของคุณจะไม่ถูกรวบรวม
pozuelog

121

หรือคุณสามารถใช้FileChannelเพื่อคัดลอกไฟล์ มันอาจจะเร็วกว่าวิธีการคัดลอกไบต์เมื่อคัดลอกไฟล์ขนาดใหญ่ คุณไม่สามารถใช้งานได้หากไฟล์ของคุณมีขนาดใหญ่กว่า 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 สามารถโยนข้อยกเว้นและในกรณีที่คุณเปิดกระแสข้อมูลทิ้งไว้ เหมือนกับที่ปางกับนิมาแสดงความคิดเห็นในคำตอบที่ยอมรับ
Viktor Brešan

นอกจากนี้ transferTo ควรถูกเรียกใช้ภายในการวนเนื่องจากไม่รับประกันว่าจะโอนยอดรวมที่ร้องขอ
Viktor Brešan

ฉันลองวิธีการแก้ปัญหาของคุณและมันก็ล้มเหลวสำหรับฉันด้วยข้อยกเว้นjava.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)ในFileOutputStream outStream = new FileOutputStream(dst);ขั้นตอน ตามข้อความที่ฉันรู้ว่าไฟล์ไม่มีอยู่ฉันจึงตรวจสอบและโทรdst.mkdir();ถ้าจำเป็น แต่ก็ยังไม่ช่วย ฉันยังพยายามที่จะตรวจสอบและมันกลับมาdst.canWrite(); falseนี่อาจเป็นสาเหตุของปัญหาหรือไม่ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>และใช่ฉันมี
Mike B.

1
@ ViktorBrešanหลังจาก API 19 คุณสามารถใช้การจัดการทรัพยากร Java อัตโนมัติโดยการกำหนดสตรีมเข้าและออกในการเปิดลองtry ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
AlbertMarkovski

มีวิธีใดบ้างที่จะทำให้โซลูชันนี้เผยแพร่ความคืบหน้าไปยังonProgressUpdateดังนั้นฉันสามารถแสดงใน ProgressBar ได้หรือไม่ ในโซลูชันที่ยอมรับฉันสามารถคำนวณความคืบหน้าได้ในขณะที่วนรอบ แต่ฉันไม่เห็นวิธีการทำที่นี่
George Bezerra

31

ขยาย Kotlin สำหรับมัน

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

นี่เป็นคำตอบที่กระชับและยืดหยุ่นที่สุด contentResolver.openInputStream(uri)คำตอบง่ายล้มเหลวในการบัญชีสำหรับยูริเปิดผ่าน
AjahnCharles

6
Kotlin คือความรัก Kotlin คือชีวิต!
Dev Aggarwal

17

สิ่งเหล่านี้ใช้ได้ดีสำหรับฉัน

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

นี่เป็นเรื่องง่ายบน Android O (API 26) ตามที่คุณเห็น:

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

10

มันอาจจะสายเกินไปสำหรับคำตอบ แต่วิธีที่สะดวกที่สุดคือการใช้

FileUtils's

static void copyFile(File srcFile, File destFile)

เช่นนี่คือสิ่งที่ฉันทำ

`

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 ไม่มีอยู่ใน Android
The Berga

2
แต่มีห้องสมุดที่ทำโดย Apache ที่ทำสิ่งนี้และอีกมากมาย: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/ ......
Alessandro Muzzi

7

ง่ายขึ้นมากตอนนี้ด้วย Kotlin:

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

trueหรือใช้falseสำหรับเขียนทับไฟล์ปลายทาง

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


ไม่ง่ายถ้าไฟล์ของคุณมาจาก URI เจตนาของแกลเลอรี
AjahnCharles

อ่านความคิดเห็นด้านล่างคำตอบ: "คำตอบนี้เป็นอันตรายอย่างแข็งขันและไม่สมควรได้รับคะแนนโหวตมันล้มเหลวหาก Uri เป็นเนื้อหา: // หรือ Uri อื่นที่ไม่ใช่ไฟล์" (ฉันทำ upvote - ฉันแค่ต้องการชี้แจงว่าไม่ใช่กระสุนเงิน)
AjahnCharles

5

นี่คือวิธีการแก้ปัญหาที่ปิดช่องรับ - ส่งสัญญาณจริงหากเกิดข้อผิดพลาดขณะคัดลอก วิธีการแก้ปัญหานี้ใช้วิธีการ apache Commons IO IOUtils สำหรับทั้งการคัดลอกและการจัดการการปิดกระแส

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

มันดูเรียบง่าย
Serg Burlaka

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.