Windows에서 신뢰할 수있는 File.renameTo () 대안?


92

Java File.renameTo()는 특히 Windows에서 문제가있는 것 같습니다. 현상태대로 API 문서가 말한다

이 방법의 동작의 많은 측면은 본질적으로 플랫폼에 따라 다릅니다. 이름 바꾸기 작업은 한 파일 시스템에서 다른 파일 시스템으로 파일을 이동할 수없고 원 자성이 아닐 수 있으며 대상 추상 경로 이름을 가진 파일이있는 경우 성공하지 못할 수 있습니다. 이미 존재 함. 반환 값은 항상 이름 바꾸기 작업이 성공했는지 확인해야합니다.

필자의 경우 업그레이드 절차의 일부로 기가 바이트의 데이터 (다양한 하위 디렉터리 및 다양한 크기의 파일)를 포함 할 수있는 디렉터리를 이동 (이름 변경)해야합니다. 이동은 항상 동일한 파티션 / 드라이브 내에서 이루어 지므로 디스크의 모든 파일을 물리적으로 이동할 필요가 없습니다.

이동할 디렉토리의 내용에 대한 파일 잠금 이 없어야 하지만, 여전히 renameTo ()가 작업을 수행하지 못하고 false를 반환합니다. (나는 아마도 일부 파일 잠금이 Windows에서 다소 임의로 만료 될 것이라고 추측하고 있습니다.)

현재 복사 및 삭제를 사용하는 대체 방법이 있지만 폴더 크기에 따라 시간 이 많이 걸릴 수 있기 때문에 짜증납니다 . 또한 잠재적으로 몇 시간 동안 기다리지 않도록 사용자가 폴더를 수동으로 이동할 수 있다는 사실을 단순히 문서화하는 것을 고려하고 있습니다. 그러나 올바른 방법은 분명히 자동적이고 빠른 것이 될 것입니다.

그래서 제 질문은 일반 JDK 또는 일부 외부 라이브러리를 사용하여 Windows 에서 Java로 빠른 이동 / 이름 변경을 수행하는 대안적이고 안정적인 접근 방식을 알고 있습니까 ? 또는 주어진 폴더와 모든 내용 (아마도 수천 개의 개별 파일)에 대한 파일 잠금을 감지하고 해제 하는 쉬운 방법을 알고있는 경우 에도 괜찮습니다.


편집 :이 특별한 경우 renameTo()에는 몇 가지만 더 고려 하여 사용하지 않은 것 같습니다 . 이 답변을 참조하십시오 .


3
훨씬 더 나은 파일 시스템 지원을 제공하는 JDK 7을 기다리거나 사용할 수 있습니다.
akarnokd

@ kd304, 실제로 기다릴 수 없거나 얼리 액세스 버전을 사용할 수는 없지만 그와 같은 것이 진행 중임을 아는 것이 흥미 롭습니다!
Jonik 2009-06-16

답변:


52

Files.move()JDK 7 의 방법 도 참조하십시오 .

예 :

String fileName = "MyFile.txt";

try {
    Files.move(new File(fileName).toPath(), new File(fileName).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
    Logger.getLogger(SomeClass.class.getName()).log(Level.SEVERE, null, ex);
}

7
안타깝게도 Java7이 항상 답은 아닙니다 (42가
그렇듯이

1
우분투 JDK7에서도 EBS 스토리지가있는 EC2에서 코드를 실행할 때이 문제에 직면했습니다. File.renameTo가 실패했고 File.canWrite도 실패했습니다.
saurabheights

이것은 File # renameTo ()만큼 신뢰할 수 없습니다. 실패하면 더 유용한 오류를 제공합니다. 내가 찾은 합리적으로 신뢰할 수있는 유일한 방법은 Files # copy가있는 파일을 새 이름으로 복사 한 다음 Files # delete를 사용하여 원본을 삭제하는 것입니다 (파일 # 이동이 실패 할 수있는 동일한 이유로 자체 삭제도 실패 할 수 있음). .
jwenting

26

그만한 가치에 대해 몇 가지 추가 개념 :

  1. Windows renameTo()에서 대상 디렉토리가 비어 있더라도 실패한 것처럼 보입니다. 이것은 내가 리눅스에서 시도했던 것처럼 나를 놀라게했다. renameTo()타겟이 존재한다면 성공했다.

    (분명히 저는 이런 종류의 것이 플랫폼간에 동일하게 작동한다고 가정해서는 안됩니다. 이것이 바로 Javadoc이 경고하는 것입니다.)

  2. 느린 파일 잠금이 있다고 의심되는 경우 이동 / 이름 변경 전에 잠시 기다리면 도움 이 될 수 있습니다. (설치 프로그램 / 업그레이 더의 한 지점에서 일부 파일에 서비스가 중단 될 수 있기 때문에 약 10 초 동안 "휴면"작업과 불확실한 진행률 표시 줄을 추가했습니다.) 을 시도한 renameTo()다음 작업이 성공하거나 시간 초과에 도달 할 때까지 일정 기간 (점진적으로 증가 할 수 있음)을 기다리는 간단한 재시도 메커니즘을 수행 할 수도 있습니다.

필자의 경우 대부분의 문제는 위의 두 가지를 모두 고려하여 해결 된 것처럼 보이므로 결국 네이티브 커널 호출이나 그런 작업을 수행 할 필요가 없습니다.


2
나는 우리의 경우에 도움이 된 것을 설명하기 때문에 지금은 내 대답을 수락하고 있습니다. 그래도 누군가 renameTo ()와 관련된보다 일반적인 문제에 대한 훌륭한 답변을 제시하면 언제든지 게시 해 주시면 받아 들여진 답변을 재고 해 드리겠습니다.
Jonik

4
6.5 년 후, 특히 많은 사람들이 도움이된다고 생각하기 때문에 JDK 7 답변을 대신 받아 들일 때라고 생각합니다 . =)
Jonik

19

원래 게시물은 "일반 JDK 또는 일부 외부 라이브러리를 사용하여 Windows에서 Java를 사용하여 빠른 이동 / 이름 변경을 수행 할 수있는 대안적이고 안정적인 접근 방식"을 요청했습니다.

아직 언급되지 않은 또 다른 옵션 은 FileUtils.moveFile () 을 포함 하는 apache.commons.io 라이브러리 의 v1.3.2 이상 입니다.

오류 발생시 부울 false를 반환하는 대신 IOException이 발생합니다.

이 다른 스레드 에서 big lep 의 응답을 참조하십시오 .


2
또한 JDK 1.7에는 더 나은 파일 시스템 I / O 지원이 포함될 것으로 보입니다. java.nio.file.Path.moveTo ()를 확인하십시오. java.sun.com/javase/7/docs/api/java/nio/file/Path.html
MykennaC

2
JDK 1.7에는 메서드가 없습니다java.nio.file.Path.moveTo()
Malte Schwerhoff

5

제 경우에는 해당 파일에 대한 핸들을 유지하는 내 응용 프로그램 내에서 죽은 개체 인 것처럼 보였습니다. 그래서 그 솔루션이 저에게 효과적이었습니다.

for (int i = 0; i < 20; i++) {
    if (sourceFile.renameTo(backupFile))
        break;
    System.gc();
    Thread.yield();
}

장점 : 특정 하드 코딩 된 시간을 가진 Thread.sleep ()이 없기 때문에 매우 빠릅니다.

단점 : 20 개로 제한하는 것은 하드 코딩 된 숫자입니다. 내 모든 테스트에서 i = 1이면 충분합니다. 하지만 확실히하기 위해 20시에 두었습니다.


1
나는 비슷한 일을했지만 루프에서 100ms의 수면을 취했습니다.
Lawrence Dol 2014 년

4

나는 이것이 약간 엉망인 것처럼 보이지만 내가 그것을 필요로했기 때문에 버퍼링 된 독자와 작가는 파일을 만드는 데 아무런 문제가없는 것 같습니다.

void renameFiles(String oldName, String newName)
{
    String sCurrentLine = "";

    try
    {
        BufferedReader br = new BufferedReader(new FileReader(oldName));
        BufferedWriter bw = new BufferedWriter(new FileWriter(newName));

        while ((sCurrentLine = br.readLine()) != null)
        {
            bw.write(sCurrentLine);
            bw.newLine();
        }

        br.close();
        bw.close();

        File org = new File(oldName);
        org.delete();

    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

}

구문 분석기의 일부로 작은 텍스트 파일에 적합합니다. oldName 및 newName이 파일 위치의 전체 경로인지 확인하십시오.

건배 Kactus


4

다음 코드는 '대안'이 아니지만 Windows 및 Linux 환경 모두에서 안정적으로 작동했습니다.

public static void renameFile(String oldName, String newName) throws IOException {
    File srcFile = new File(oldName);
    boolean bSucceeded = false;
    try {
        File destFile = new File(newName);
        if (destFile.exists()) {
            if (!destFile.delete()) {
                throw new IOException(oldName + " was not successfully renamed to " + newName); 
            }
        }
        if (!srcFile.renameTo(destFile))        {
            throw new IOException(oldName + " was not successfully renamed to " + newName);
        } else {
                bSucceeded = true;
        }
    } finally {
          if (bSucceeded) {
                srcFile.delete();
          }
    }
}

2
음,이 코드는 renameTo (또는 destFile.delete)가 실패하고 메서드가 IOException을 throw하더라도 srcFile을 삭제합니다. 그게 좋은 생각인지 잘 모르겠습니다.
Jonik

1
@Jonik, Thanx, 이름 변경이 실패 할 경우 src 파일을 삭제하지 않도록 코드를 수정했습니다.
미친 말

공유 해주셔서 감사합니다.
BillMan 2011 년

3

Windows Runtime.getRuntime().exec("cmd \\c ")에서는 명령 줄 이름 바꾸기 기능을 사용하여 실제로 파일 이름을 바꿉니다. 훨씬 더 유연합니다. 예를 들어 dir에있는 모든 txt 파일의 확장자 이름을 bak로 바꾸려면 출력 스트림에 다음과 같이 작성하면됩니다.

* .txt * .bak 이름 바꾸기

나는 그것이 좋은 해결책이 아니라는 것을 알고 있지만 분명히 Java 인라인 지원보다 훨씬 더 나아졌습니다.


슈퍼, 훨씬 낫다! 감사! :-)
gaffcz 2013 년

2

왜 ....

import com.sun.jna.Native;
import com.sun.jna.Library;

public class RenamerByJna {
    /* Requires jna.jar to be in your path */

    public interface Kernel32 extends Library {
        public boolean MoveFileA(String existingFileName, String newFileName);
    }

    public static void main(String[] args) {
        String path = "C:/yourchosenpath/";
        String existingFileName = path + "test.txt";
        String newFileName = path + "renamed.txt";

        Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
            kernel32.MoveFileA(existingFileName, newFileName);
        }
}

nwindows 7에서 작동합니다. existingFile이 없으면 아무 작업도 수행하지 않지만 분명히이 문제를 해결하기 위해 더 나은 도구를 사용할 수 있습니다.


2

비슷한 문제가있었습니다. 파일은 Windows에서 이동하는 대신 복사되었지만 Linux에서는 잘 작동했습니다. renameTo ()를 호출하기 전에 열린 fileInputStream을 닫아 문제를 해결했습니다. Windows XP에서 테스트되었습니다.

fis = new FileInputStream(originalFile);
..
..
..
fis.close();// <<<---- Fixed by adding this
originalFile.renameTo(newDesitnationForOriginalFile);

1

제 경우에는 오류가 상위 디렉토리의 경로에있었습니다. 버그 일 수도 있고 올바른 경로를 얻기 위해 하위 문자열을 사용해야했습니다.

        try {
            String n = f.getAbsolutePath();
            **n = n.substring(0, n.lastIndexOf("\\"));**
            File dest = new File(**n**, newName);
            f.renameTo(dest);
        } catch (Exception ex) {
           ...

0

짜증나는 건 알지만 대안은 "성공"또는 "오류"와 같은 간단한 것을 출력하는 bat 스크립트를 만들고 호출하고 실행될 때까지 기다린 다음 결과를 확인하는 것입니다.

Runtime.getRuntime (). exec ( "cmd / c start test.bat");

이 스레드는 흥미로울 수 있습니다. 다른 프로세스의 콘솔 출력을 읽는 방법에 대한 Process 클래스도 확인하십시오.


-2

robocopy를 시도 할 수 있습니다 . 이것은 정확히 "이름 변경"이 아니지만 매우 신뢰할 수 있습니다.

Robocopy는 디렉터리 또는 디렉터리 트리의 안정적인 미러링을 위해 설계되었습니다. 모든 NTFS 특성 및 속성이 복사되도록하는 기능이 있으며 중단 될 수있는 네트워크 연결을위한 추가 재시작 코드가 포함되어 있습니다.


감사. 하지만 robocopy는 Java 라이브러리가 아니기 때문에 아마도 내 Java 코드에서 사용 (번들)하기가 그리 쉽지 않을 것입니다.
Jonik

-2

파일을 이동하거나 이름을 바꾸려면이 기능을 사용할 수 있습니다.

BOOL WINAPI MoveFile(
  __in  LPCTSTR lpExistingFileName,
  __in  LPCTSTR lpNewFileName
);

kernel32.dll에 정의되어 있습니다.


1
나는 이것을 JNI로 감싸는 문제를 겪는 것이 Process 데코레이터에서 robocopy를 감싸는 데 필요한 노력보다 더 크다고 생각합니다.
Kevin Montrose

네, 이것은 추상화에 대해 지불하는 대가입니다. 그리고 그것이 누출되면 좋은 누출이 발생합니다 = D
Chii

감사합니다. 너무 복잡하지 않으면 고려할 수 있습니다. 저는 JNI를 사용 해본 적이없고 SO에서 Windows 커널 함수를 호출하는 좋은 예를 찾을 수 없었기 때문에이 질문을 게시했습니다. stackoverflow.com/questions/1000723/…
Jonik

이것은 매우 간단한 함수 호출 이므로 johannburkard.de/software/nativecall 과 같은 일반 JNI 래퍼를 시도 할 수 있습니다.
Peter Smith

-8
 File srcFile = new File(origFilename);
 File destFile = new File(newFilename);
 srcFile.renameTo(destFile);

위는 간단한 코드입니다. Windows 7에서 테스트했으며 완벽하게 작동합니다.


11
renameTo ()가 안정적으로 작동 하지 않는 경우가 있습니다 . 그것이 질문의 요점입니다.
Jonik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.