Programing

Java에서 파일을 복사하는 표준 간결한 방법?

c10106 2022. 4. 24. 09:46
반응형

Java에서 파일을 복사하는 표준 간결한 방법?

자바에서 파일을 복사하는 유일한 방법은 스트림을 열고, 버퍼를 선언하고, 한 파일로 읽고, 루핑하고, 다른 스팀에게 그것을 쓰는 것밖에 없다는 것이 항상 나를 괴롭혔다.웹에는 이와 유사하지만, 여전히 약간 다른 유형의 솔루션 구현이 흩어져 있다.

Java 언어의 범위 내에 머무르는 더 나은 방법이 있는가? (즉, OS 특정 명령을 실행하지 않는 것)적어도 이러한 기본 구현을 모호하게 하고 단일 라인 솔루션을 제공하는 신뢰할 수 있는 오픈 소스 유틸리티 패키지에서?

나는 아파치 커먼즈 같은 메가 api를 사용하지 않을 것이다.이것은 새로운 NIO 패키지의 JDK에 내장된 간단한 작업이다.그것은 이전의 답변에서 이미 어느 정도 연결되어 있었지만, 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를 하루아침에 배우려고 하기 전에 이 정비사를 신뢰하는 것이 좋을 것이다.개인적인 경험으로 볼 때 경험이 없고 java.io 스트림을 통해 IO에 소개된 경우 학습하기가 매우 어려울 수 있다.

툴킷에서 위에서 언급했듯이 Apache Commons IO는 특히 FileUtils.copyFile()을 사용하는 방법으로, 모든 무거운 리프팅을 처리한다.

그리고 추신으로 FileUtils의 최근 버전(예: 2.0.1 릴리즈)은 파일 복사에 NIO의 사용을 추가했다는 점에 유의하십시오. NIO 루틴은 Java 계층을 통해 바이트를 읽고 쓰는 것보다 OS/파일 시스템에 직접 복사를 연기하기 때문에 파일 복사 성능을 크게 높일있다.따라서 성능을 원하는 경우 최신 버전의 FileUtils를 사용하고 있는지 확인해 보십시오.

이제 Java 7에서는 다음과 같은 리소스 사용 시도 구문을 사용할 수 있다.

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

꽤 섹시하지, 응?

  • 이러한 방법은 성능 설계(운영 체제 기본 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 );

자바 7에서는 쉽다...

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

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

파일을 복사하여 대상 경로에 저장하려면 아래 방법을 사용하십시오.

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

이러한 메커니즘은 모두 파일의 내용만 복사하고 사용 권한과 같은 메타데이터는 복사하지 않는다는 점에 유의하십시오.따라서 만약 당신이 Linux에서 실행 가능한 .sh 파일을 복사하거나 이동한다면, 새로운 파일은 실행이 되지 않을 것이다.

파일을 진정으로 복사하거나 이동하려면, 즉 명령줄에서 복사하는 것과 같은 결과를 얻으려면 실제로 네이티브 도구를 사용해야 한다.셸 스크립트 또는 JNI.

분명히, 이것은 자바 7 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html에서 고쳐질 수 있다.행운을 빌어!

구글의 구아바 도서관에도 다음과 같은 복사 방법이 있다.

공개 정적 무효 사본(파일 원본,파일 대상)IOException을 던지다
한 파일에서 다른 파일로 모든 바이트 복사

경고:만약to기존 파일을 나타내며, 해당 파일은 다음 내용으로 덮어쓰게 된다.from. 만약to그리고from같은 파일을 참조하면 해당 파일의 내용이 삭제된다.

매개 변수:from 파일to

던지기: IOException- I/O 오류가 발생하는 경우IllegalArgumentException- 만약from.equals(to)

Java 7, path.copy에서 표준으로 사용 가능받는 사람: http://openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/tutorial/essential/io/copy.html

파일 복사처럼 평범하고 간단한 것을 표준화하는데 그렇게 오랜 시간이 걸렸다니 믿을 수 없다. ()

위의 코드에서 발생할 수 있는 세 가지 문제:

  1. getChannel에서 예외가 발생하면 열린 스트림이 누출될 수 있다.
  2. 대용량 파일의 경우 OS에서 처리할 수 있는 것보다 많은 파일을 한 번에 전송하려고 할 수 있다.
  3. transferFrom의 반환 값을 무시하고 있으므로 파일의 일부만 복사하는 것일 수 있음.

이래서다.org.apache.tools.ant.util.ResourceUtils.copyResource너무 복잡해transferFrom이 정상인 동안 Linux에서 JDK 1.4에서 transferTo가 중단됨(Bug ID:5056395 참조) – Jesse Glick Jan

이미 Spring을 사용하는 웹 응용 프로그램에 있고 간단한 파일 복사에 Apache Commons IO를 포함하지 않으려면 Spring 프레임워크의 FileCopyUtils를 사용하십시오.

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

여기 코드 한 줄로 파일을 쉽게 복사할 수 있는 세 가지 방법이 있다!

Java7:

java.nio.file.파일#복사

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

내 테스트에 따르면 버퍼와 함께 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와 함께 작동하며 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();
}

파티에 조금 늦었지만, 여기 다양한 파일 복사 방법을 사용하여 파일을 복사하는 데 걸리는 시간을 비교해보자.나는 그 방법들을 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 채널 클래스를 사용하면서 볼 수 있는 유일한 단점은 여전히 중간 파일 복사 진행률을 표시할 방법을 찾을 수 없다는 것이다.

참조URL: https://stackoverflow.com/questions/106770/standard-concise-way-to-copy-a-file-in-java

반응형