วิธีรัดกุมมาตรฐานในการคัดลอกไฟล์ใน Java?


421

มันทำให้ฉันรำคาญอยู่เสมอว่าวิธีเดียวที่จะคัดลอกไฟล์ใน Java เกี่ยวข้องกับการเปิดสตรีมการประกาศบัฟเฟอร์การอ่านในไฟล์เดียววนรอบมันและเขียนมันออกไปยังไอน้ำอื่น ๆ เว็บถูกทิ้งให้เหมือนกัน แต่ก็ยังมีการใช้งานที่แตกต่างกันเล็กน้อยของการแก้ปัญหาประเภทนี้

มีวิธีที่ดีกว่าที่จะอยู่ในขอบเขตของภาษา Java (ความหมายไม่เกี่ยวข้องกับคำสั่งเฉพาะระบบปฏิบัติการ exec) บางทีในแพคเกจยูทิลิตี้โอเพนซอร์สที่น่าเชื่อถืออย่างน้อยที่สุดจะปิดบังการใช้งานพื้นฐานและให้บริการโซลูชั่นเดียว?


5
อาจจะมีบางสิ่งบางอย่างใน Apache Commons FileUtils , เฉพาะcopyFileวิธี
ชุดเครื่องมือ

22
หากใช้ Java 7 ให้ใช้ Files.copy แทนตามที่แนะนำโดย @GlenBest: stackoverflow.com/a/16600787/44737
rob

คำตอบ:


274

ในฐานะที่เป็นเครื่องมือที่กล่าวถึงข้างต้น Apache Commons IO เป็นวิธีที่จะไปเฉพาะFileUtils copyFile () ; มันจัดการการยกของหนักทั้งหมดสำหรับคุณ

และในฐานะที่เป็นคำลงท้ายโปรดทราบว่าเวอร์ชันล่าสุดของ FileUtils (เช่นรุ่น 2.0.1) ได้เพิ่มการใช้ NIO สำหรับการคัดลอกไฟล์ NIO สามารถเพิ่มประสิทธิภาพการคัดลอกไฟล์อย่างมีนัยสำคัญส่วนใหญ่เป็นเพราะงานประจำ NIO เลื่อนการคัดลอกโดยตรงไปยังระบบปฏิบัติการ / ระบบไฟล์แทนที่จะจัดการมันโดยการอ่านและการเขียนไบต์ผ่านชั้น Java ดังนั้นหากคุณกำลังมองหาประสิทธิภาพมันอาจคุ้มค่าที่จะตรวจสอบว่าคุณใช้ FileUtils เวอร์ชันล่าสุด


1
มีประโยชน์มาก - คุณมีความเข้าใจหรือไม่ว่าการเปิดตัวเป็นทางการจะรวมการเปลี่ยนแปลง nio เหล่านี้หรือไม่?
ปีเตอร์

2
Apache Commons IO สาธารณะยังคงอยู่ที่ 1.4, grrrrrrr
Peter

14
ณ เดือนธันวาคม 2010 Apache Commons IO อยู่ที่ 2.0.1 ซึ่งมีฟังก์ชันการทำงานของ NIO อัปเดตคำตอบแล้ว
Simon Nickerson

4
คำเตือนสำหรับผู้ใช้ Android: ไม่รวมอยู่ใน Android API มาตรฐาน
IlDan

18
หากใช้ Java 7 หรือใหม่กว่าคุณสามารถใช้ Files.copy ตามที่แนะนำโดย @GlenBest: stackoverflow.com/a/16600787/44737
rob

278

ฉันจะหลีกเลี่ยงการใช้ api เมกะเช่น apache ทั่วไป นี่เป็นการดำเนินการที่ง่ายและสร้างขึ้นใน JDK ในแพ็คเกจ NIO ใหม่ มันเชื่อมโยงกับคำตอบก่อนหน้านี้แล้ว แต่วิธีการสำคัญใน NIO api คือฟังก์ชั่นใหม่ "transferTo" และ "transferFrom"

http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html#transferTo(long,%20long,%20java.nio.channels.WritableByteChannel)

หนึ่งในบทความที่เชื่อมโยงแสดงวิธีที่ยอดเยี่ยมในการรวมฟังก์ชั่นนี้เข้ากับโค้ดของคุณโดยใช้ transferFrom:

public static void copyFile(File sourceFile, File destFile) throws IOException {
    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();
        }
    }
}

การเรียนรู้ NIO อาจเป็นเรื่องยุ่งยากเล็กน้อยดังนั้นคุณอาจต้องการวางใจในกลไกนี้ก่อนที่จะออกไปและพยายามเรียนรู้ NIO ในชั่วข้ามคืน จากประสบการณ์ส่วนตัวอาจเป็นเรื่องยากที่จะเรียนรู้หากคุณไม่มีประสบการณ์และได้รับการแนะนำให้รู้จักกับ IO ผ่านสตรีม java.io


2
ขอบคุณข้อมูลที่มีประโยชน์ ฉันจะยังคงโต้แย้งอะไรบางอย่างเช่น Apache Commons โดยเฉพาะถ้าใช้ nio (ถูกต้อง) ด้านล่าง แต่ฉันยอมรับว่ามันเป็นสิ่งสำคัญที่จะเข้าใจพื้นฐานพื้นฐาน
ปีเตอร์

1
น่าเสียดายที่มีข้อแม้ เมื่อฉันคัดลอกไฟล์ 1.5 Gb บน Windows 7, 32 บิตมันไม่สามารถแมปไฟล์ได้ ฉันต้องหาทางออกอื่น
Anton K.

15
ปัญหาที่เป็นไปได้สามประการด้วยรหัสด้านบน: (a) ถ้า getChannel ส่งข้อยกเว้นคุณอาจรั่วกระแสข้อมูล (b) สำหรับไฟล์ขนาดใหญ่คุณอาจพยายามถ่ายโอนมากกว่าครั้งเดียวที่ระบบปฏิบัติการสามารถจัดการได้ (c) คุณไม่สนใจค่าส่งคืนของ transferFrom ดังนั้นอาจเป็นการคัดลอกเพียงส่วนหนึ่งของไฟล์ นี่คือเหตุผลที่ org.apache.tools.ant.util.ResourceUtils.copyResource นั้นซับซ้อน นอกจากนี้โปรดทราบว่าในขณะที่การถ่ายโอนจากเป็นปกติให้โอนย้ายเพื่อหยุดบน JDK 1.4 บน Linux: bugs.sun.com/bugdatabase/view_bug.do?bug_id=5056395
Jesse Glick

7
ฉันเชื่อว่าเวอร์ชันที่อัปเดตนี้ตอบสนอง
Mark Renouf

11
รหัสนี้มีปัญหาที่สำคัญ transferTo () ต้องถูกเรียกในลูป ไม่รับประกันการโอนเงินตามจำนวนที่ร้องขอ
มาร์ควิสแห่ง Lorne

180

ขณะนี้ใช้ Java 7 คุณสามารถใช้ไวยากรณ์ try-with-resource ต่อไปนี้:

public static void copyFile( File from, File to ) throws IOException {

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

    try (
        FileChannel in = new FileInputStream( from ).getChannel();
        FileChannel out = new FileOutputStream( to ).getChannel() ) {

        out.transferFrom( in, 0, in.size() );
    }
}

หรือดีกว่านี้สามารถทำได้โดยใช้คลาสไฟล์ใหม่ที่นำเสนอใน Java 7:

public static void copyFile( File from, File to ) throws IOException {
    Files.copy( from.toPath(), to.toPath() );
}

งี่เง่าสวยใช่มั้ย?


15
มันยอดเยี่ยมมากที่ Java ไม่ได้เพิ่มสิ่งนี้ในวันนี้ การดำเนินการบางอย่างเป็นเพียงสิ่งจำเป็นอย่างยิ่งในการเขียนซอฟต์แวร์คอมพิวเตอร์ นักพัฒนา Java ของ Oracle สามารถเรียนรู้สิ่งหนึ่งหรือสองอย่างจากระบบปฏิบัติการโดยดูที่บริการที่มีให้เพื่อให้ง่ายต่อการย้ายมือใหม่
Rick Hodgin

2
อ้าขอบคุณ! ฉันไม่รู้จักคลาส "Files" ใหม่ที่มีฟังก์ชันตัวช่วยทั้งหมด มันมีสิ่งที่ฉันต้องการ ขอบคุณสำหรับตัวอย่าง
ChrisCantrell

1
ประสิทธิภาพฉลาด, Java NIO FileChannel ดีกว่าอ่านบทความนี้journaldev.com/861/4-ways-to-copy-file-in-java
Pankaj

5
รหัสนี้มีปัญหาที่สำคัญ transferTo () ต้องถูกเรียกในลูป ไม่รับประกันการโอนเงินตามจำนวนที่ร้องขอ
มาร์ควิสแห่ง Lorne

@Scott: Pete ขอวิธีแก้ปัญหาแบบบรรทัดเดียวและคุณสนิทมาก ... ฉันเพิ่งจะวาง Files.copy (Path from, Path to) ที่จุดเริ่มต้นของคำตอบของคุณและพูดถึงว่าคุณสามารถใช้ File.toPath () ถ้าคุณมีวัตถุ File ที่มีอยู่: Files.copy (fromFile.toPath () toFile.toPath ())
ปล้น

89
  • วิธีการเหล่านี้ได้รับการออกแบบมาเพื่อประสิทธิภาพ (รวมเข้ากับ I / O ระบบปฏิบัติการดั้งเดิม)
  • วิธีการเหล่านี้ทำงานกับไฟล์ไดเรกทอรีและลิงก์
  • แต่ละตัวเลือกที่ให้มาอาจถูกทิ้งไว้ - เป็นทางเลือก

คลาสยูทิลิตี้

package com.yourcompany.nio;

class Files {

    static int copyRecursive(Path source, Path target, boolean prompt, CopyOptions options...) {
        CopyVisitor copyVisitor = new CopyVisitor(source, target, options).copy();
        EnumSet<FileVisitOption> fileVisitOpts;
        if (Arrays.toList(options).contains(java.nio.file.LinkOption.NOFOLLOW_LINKS) {
            fileVisitOpts = EnumSet.noneOf(FileVisitOption.class) 
        } else {
            fileVisitOpts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
        }
        Files.walkFileTree(source[i], fileVisitOpts, Integer.MAX_VALUE, copyVisitor);
    }

    private class CopyVisitor implements FileVisitor<Path>  {
        final Path source;
        final Path target;
        final CopyOptions[] options;

        CopyVisitor(Path source, Path target, CopyOptions options...) {
             this.source = source;  this.target = target;  this.options = options;
        };

        @Override
        FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        // before visiting entries in a directory we copy the directory
        // (okay if directory already exists).
        Path newdir = target.resolve(source.relativize(dir));
        try {
            Files.copy(dir, newdir, options);
        } catch (FileAlreadyExistsException x) {
            // ignore
        } catch (IOException x) {
            System.err.format("Unable to create: %s: %s%n", newdir, x);
            return SKIP_SUBTREE;
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        Path newfile= target.resolve(source.relativize(file));
        try {
            Files.copy(file, newfile, options);
        } catch (IOException x) {
            System.err.format("Unable to copy: %s: %s%n", source, x);
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        // fix up modification time of directory when done
        if (exc == null && Arrays.toList(options).contains(COPY_ATTRIBUTES)) {
            Path newdir = target.resolve(source.relativize(dir));
            try {
                FileTime time = Files.getLastModifiedTime(dir);
                Files.setLastModifiedTime(newdir, time);
            } catch (IOException x) {
                System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
            }
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        if (exc instanceof FileSystemLoopException) {
            System.err.println("cycle detected: " + file);
        } else {
            System.err.format("Unable to copy: %s: %s%n", file, exc);
        }
        return CONTINUE;
    }
}

คัดลอกไดเรกทอรีหรือไฟล์

long bytes = java.nio.file.Files.copy( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES,
                 java.nio.file.LinkOption.NOFOLLOW_LINKS);

ย้ายไดเรกทอรีหรือไฟล์

long bytes = java.nio.file.Files.move( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.ATOMIC_MOVE,
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING);

การคัดลอกไดเรกทอรีหรือไฟล์แบบเรียกซ้ำ

long bytes = com.yourcompany.nio.Files.copyRecursive( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES
                 java.nio.file.LinkOption.NOFOLLOW_LINKS );

ชื่อแพ็กเกจสำหรับ Files ผิด (ควรเป็น java.nio.file ไม่ใช่ java.nio) ฉันได้ส่งการแก้ไขสำหรับสิ่งนั้นแล้ว หวังว่าไม่เป็นไร!
Stuart Rossiter

43

ใน Java 7 มันง่าย ...

File src = new File("original.txt");
File target = new File("copy.txt");

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);

1
คำตอบของคุณเพิ่มไปยัง Scott หรือ Glen's คืออะไร?
Uri Agassi

11
มันกระชับน้อยกว่ามาก คำตอบของพวกเขาดีและมีรายละเอียด แต่ฉันคิดถึงพวกเขาเมื่อมองผ่าน น่าเสียดายที่มีคำตอบมากมายเกี่ยวกับเรื่องนี้และหลายคำตอบนั้นยาวล้าสมัยและซับซ้อนและคำตอบที่ดีของ Scott and Glen ก็หายไป (ฉันจะให้ผู้โหวตขึ้นเพื่อช่วยในเรื่องนั้น) ฉันสงสัยว่าคำตอบของฉันอาจได้รับการปรับปรุงให้ดีขึ้นโดยลดเป็นสามบรรทัดหรือไม่โดยการเคาะข้อความ () และข้อความแสดงข้อผิดพลาดที่มีอยู่
Kevin Sadler

สิ่งนี้ใช้ไม่ได้กับไดเรกทอรี ทุกคนประณามสิ่งนี้ผิด การสื่อสาร API ที่มากขึ้นทำให้คุณเป็นฝ่ายผิด ฉันก็เข้าใจผิด
mmm

2
@momo คำถามคือวิธีการคัดลอกไฟล์
Kevin Sadler

28

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

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

1
มันจะใช้งานได้ แต่ฉันไม่คิดว่ามันจะดีไปกว่าคำตอบอื่น ๆ ที่นี่?
Rup

2
@Rup มันดีกว่าคำตอบอื่น ๆ ที่นี่ (ก) เพราะมันใช้งานได้และ (b) เพราะมันไม่ได้ใช้ซอฟต์แวร์ของบุคคลที่สาม
มาร์ควิสแห่ง Lorne

1
@EJP ตกลง แต่มันไม่ได้ฉลาดมาก การคัดลอกไฟล์ควรเป็นการดำเนินการของระบบปฏิบัติการหรือระบบไฟล์ไม่ใช่การทำงานของแอปพลิเคชัน: Java หวังว่าจะสามารถคัดลอกและเปลี่ยนเป็นการดำเนินการของระบบปฏิบัติการได้ยกเว้นโดยการอ่านไฟล์อย่างชัดเจนว่าคุณกำลังหยุดทำ หากคุณไม่คิดว่า Java สามารถทำเช่นนั้นได้คุณจะเชื่อใจได้หรือไม่ที่จะเพิ่มประสิทธิภาพการอ่านและเขียน 1K ลงในบล็อคที่ใหญ่ขึ้น? และถ้าแหล่งที่มาและปลายทางอยู่บนการแชร์จากระยะไกลบนเครือข่ายที่ช้านี่แสดงว่าเป็นการทำงานที่ไม่จำเป็น ใช่ JAR บุคคลที่สามมีขนาดใหญ่มาก (Guava!) แต่พวกเขาเพิ่มสิ่งต่าง ๆ มากมายเช่นนี้ทำได้อย่างถูกต้อง
รูปี

ทำงานเหมือนจับใจ ทางออกที่ดีที่สุดที่ไม่ต้องการไลบรารีบุคคลที่สามและใช้งานได้บน java 1.6 ขอบคุณ
James Wierzba

@Rup ฉันยอมรับว่าควรเป็นฟังก์ชั่นระบบปฏิบัติการ แต่ฉันไม่สามารถแสดงความคิดเห็นของคุณได้ ส่วนหลังโคลอนแรกขาดกริยาอยู่ที่ไหนสักแห่ง; ฉันจะไม่ 'เชื่อใจ' ไม่คาดหวังว่า Java จะเปลี่ยนบล็อกขนาด 1k เป็นสิ่งที่ใหญ่กว่าถึงแม้ว่าฉันจะใช้ตัวบล็อกที่ใหญ่กว่านี้อย่างแน่นอน ฉันจะไม่เขียนแอปพลิเคชันที่ใช้ไฟล์ร่วมกันตั้งแต่แรก และฉันไม่ทราบว่าไลบรารี่ของบุคคลที่สามจะทำอะไรที่ 'ถูกต้อง' (อะไรที่คุณหมายถึง) มากกว่าโค้ดนี้ยกเว้นอาจใช้บัฟเฟอร์ที่ใหญ่กว่า
มาร์ควิสแห่ง Lorne

24

โปรดทราบว่ากลไกเหล่านี้ทั้งหมดคัดลอกเนื้อหาของไฟล์เท่านั้นไม่ใช่ข้อมูลเมตาเช่นการอนุญาต ดังนั้นหากคุณต้องการคัดลอกหรือย้ายไฟล์. sh ที่รันได้บน linux ไฟล์ใหม่จะไม่สามารถเรียกใช้งานได้

ในการคัดลอกหรือย้ายไฟล์อย่างแท้จริงนั่นคือเพื่อให้ได้ผลลัพธ์เช่นเดียวกับการคัดลอกจากบรรทัดคำสั่งคุณจำเป็นต้องใช้เครื่องมือดั้งเดิม เป็นเชลล์สคริปต์หรือ JNI

เห็นได้ชัดว่านี้อาจได้รับการแก้ไขใน java 7 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html ไขว้นิ้วเอาไว้!


23

ห้องสมุด Guava ของ Google ยังมีวิธีการคัดลอก :

public void copy ( ไฟล์  จาก,
                        ไฟล์  ไปยัง)
                 พ่นIOException
คัดลอกไบต์ทั้งหมดจากไฟล์หนึ่งไปยังอีกไฟล์หนึ่ง

คำเตือน:ถ้าหมายถึงไฟล์ที่มีอยู่แฟ้มที่จะถูกเขียนทับกับเนื้อหาของto fromถ้าtoและ fromอ้างถึงไฟล์เดียวกันเนื้อหาของไฟล์นั้นจะถูกลบ

พารามิเตอร์:from - ไฟล์ต้นฉบับto- ไฟล์ปลายทาง

พ่น: IOException - ถ้าเกิดข้อผิดพลาด I / O IllegalArgumentException- ถ้าfrom.equals(to)


19

มีให้บริการตามมาตรฐานใน Java 7, path.copyTo: http://openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/ กวดวิชา / จำเป็น / io / copy.html

ฉันไม่อยากจะเชื่อเลยว่ามันใช้เวลานานมากที่จะสร้างสิ่งที่ธรรมดาและเรียบง่ายเหมือนการคัดลอกไฟล์ :(


10
ไม่มี Path.copyTo; มันเป็น Files.copy
Jesse Glick

7

สามปัญหาที่เป็นไปได้กับรหัสข้างต้น:

  1. ถ้า getChannel ส่งข้อยกเว้นคุณอาจรั่วกระแสข้อมูลที่เปิดอยู่
  2. สำหรับไฟล์ขนาดใหญ่คุณอาจพยายามถ่ายโอนมากกว่าครั้งเดียวที่ระบบปฏิบัติการสามารถจัดการได้
  3. คุณไม่สนใจค่าส่งคืนของ transferFrom ดังนั้นจึงอาจเป็นการคัดลอกเพียงส่วนหนึ่งของไฟล์

นี่คือเหตุผลที่org.apache.tools.ant.util.ResourceUtils.copyResourceซับซ้อนมาก โปรดทราบว่าในขณะที่การถ่ายโอนจากไม่เป็นไรการถ่ายโอนเพื่อหยุดพักบน JDK 1.4 บน Linux (ดูรหัสข้อผิดพลาด: 5056395 ) - Jesse Glick Jan


7

หากคุณอยู่ในเว็บแอปพลิเคชันที่ใช้ Spring อยู่แล้วและหากคุณไม่ต้องการรวม Apache Commons IO สำหรับการคัดลอกไฟล์อย่างง่ายคุณสามารถใช้FileCopyUtilsของ Spring Framework


7

นี่คือสามวิธีที่คุณสามารถคัดลอกไฟล์ได้อย่างง่ายดายด้วยรหัสบรรทัดเดียว!

Java7 :

java.nio.file.Files สำเนา #

private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
    Files.copy(source.toPath(), dest.toPath());
}

Appache Commons IO :

FileUtils # copyFile

private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
    FileUtils.copyFile(source, dest);
}

ฝรั่ง :

คัดลอกไฟล์ #

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}

อันแรกไม่ทำงานสำหรับไดเรกทอรี ทุกคนประณามสิ่งนี้ผิด การสื่อสาร API มีปัญหามากกว่าที่คุณเป็น ฉันก็เข้าใจผิด
mmm

อันแรกต้องการ 3 พารามิเตอร์ Files.copyโดยใช้เพียง 2 พารามิเตอร์สำหรับการPath Streamเพียงแค่เพิ่มพารามิเตอร์StandardCopyOption.COPY_ATTRIBUTESหรือStandardCopyOption.REPLACE_EXISTINGสำหรับPathการPath
แมงดา Trizkit

6
public static void copyFile(File src, File dst) throws IOException
{
    long p = 0, dp, size;
    FileChannel in = null, out = null;

    try
    {
        if (!dst.exists()) dst.createNewFile();

        in = new FileInputStream(src).getChannel();
        out = new FileOutputStream(dst).getChannel();
        size = in.size();

        while ((dp = out.transferFrom(in, p, size)) > 0)
        {
            p += dp;
        }
    }
    finally {
        try
        {
            if (out != null) out.close();
        }
        finally {
            if (in != null) in.close();
        }
    }
}

ดังนั้นความแตกต่างจากคำตอบที่ได้รับการยอมรับสูงสุดคือคุณได้รับการถ่ายโอนจากลูปไปอีกสักพัก?
โฟโต้

1
ไม่ได้คอมไพล์และการเรียกใช้ createNewFile () ซ้ำซ้อนและสิ้นเปลือง
มาร์ควิสแห่ง Lorne

3

การทำสำเนา NIO ด้วยบัฟเฟอร์เป็นวิธีที่เร็วที่สุดตามการทดสอบของฉัน ดูรหัสการทำงานด้านล่างจากโครงการทดสอบของฉันที่https://github.com/mhisoft/fastcopy

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;


public class test {

private static final int BUFFER = 4096*16;
static final DecimalFormat df = new DecimalFormat("#,###.##");
public static void nioBufferCopy(final File source, final File target )  {
    FileChannel in = null;
    FileChannel out = null;
    double  size=0;
    long overallT1 =  System.currentTimeMillis();

    try {
        in = new FileInputStream(source).getChannel();
        out = new FileOutputStream(target).getChannel();
        size = in.size();
        double size2InKB = size / 1024 ;
        ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);

        while (in.read(buffer) != -1) {
            buffer.flip();

            while(buffer.hasRemaining()){
                out.write(buffer);
            }

            buffer.clear();
        }
        long overallT2 =  System.currentTimeMillis();
        System.out.println(String.format("Copied %s KB in %s millisecs", df.format(size2InKB),  (overallT2 - overallT1)));
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    finally {
        close(in);
        close(out);
    }
}

private static void close(Closeable closable)  {
    if (closable != null) {
        try {
            closable.close();
        } catch (IOException e) {
            if (FastCopy.debug)
                e.printStackTrace();
        }    
    }
}

}


ดี! อันนี้เร็วกว่าสตรีมมิ่ง java.io มาตรฐาน .. การคัดลอก 10GB ใน 160 วินาทีเท่านั้น
aswzen

2

รวดเร็วและทำงานกับ Java ทุกเวอร์ชันเช่น Android:

private void copy(final File f1, final File f2) throws IOException {
    f2.createNewFile();

    final RandomAccessFile file1 = new RandomAccessFile(f1, "r");
    final RandomAccessFile file2 = new RandomAccessFile(f2, "rw");

    file2.getChannel().write(file1.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f1.length()));

    file1.close();
    file2.close();
}

1
ระบบไฟล์บางระบบไม่รองรับไฟล์ที่แมปหน่วยความจำและฉันคิดว่ามันค่อนข้างแพงสำหรับไฟล์ขนาดเล็ก
โฟโต้

ไม่ทำงานกับ Java เวอร์ชันใด ๆ ก่อนหน้า 1.4 และไม่มีสิ่งใดที่รับประกันว่าการเขียนครั้งเดียวก็เพียงพอแล้ว
มาร์ควิสแห่ง Lorne

1

สายไปปาร์ตี้เล็กน้อย แต่นี่เป็นการเปรียบเทียบเวลาที่ใช้ในการคัดลอกไฟล์โดยใช้วิธีการคัดลอกไฟล์ที่หลากหลาย ฉันวนลูปผ่านวิธีการ 10 ครั้งและหาค่าเฉลี่ย การถ่ายโอนไฟล์โดยใช้สตรีม IO ดูเหมือนจะเป็นตัวเลือกที่แย่ที่สุด:

เปรียบเทียบการถ่ายโอนไฟล์โดยใช้วิธีการต่าง ๆ

นี่คือวิธีการ:

private static long fileCopyUsingFileStreams(File fileToCopy, File newFile) throws IOException {
    FileInputStream input = new FileInputStream(fileToCopy);
    FileOutputStream output = new FileOutputStream(newFile);
    byte[] buf = new byte[1024];
    int bytesRead;
    long start = System.currentTimeMillis();
    while ((bytesRead = input.read(buf)) > 0)
    {
        output.write(buf, 0, bytesRead);
    }
    long end = System.currentTimeMillis();

    input.close();
    output.close();

    return (end-start);
}

private static long fileCopyUsingNIOChannelClass(File fileToCopy, File newFile) throws IOException
{
    FileInputStream inputStream = new FileInputStream(fileToCopy);
    FileChannel inChannel = inputStream.getChannel();

    FileOutputStream outputStream = new FileOutputStream(newFile);
    FileChannel outChannel = outputStream.getChannel();

    long start = System.currentTimeMillis();
    inChannel.transferTo(0, fileToCopy.length(), outChannel);
    long end = System.currentTimeMillis();

    inputStream.close();
    outputStream.close();

    return (end-start);
}

private static long fileCopyUsingApacheCommons(File fileToCopy, File newFile) throws IOException
{
    long start = System.currentTimeMillis();
    FileUtils.copyFile(fileToCopy, newFile);
    long end = System.currentTimeMillis();
    return (end-start);
}

private static long fileCopyUsingNIOFilesClass(File fileToCopy, File newFile) throws IOException
{
    Path source = Paths.get(fileToCopy.getPath());
    Path destination = Paths.get(newFile.getPath());
    long start = System.currentTimeMillis();
    Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
    long end = System.currentTimeMillis();

    return (end-start);
}

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

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