CodeGym /Các khóa học /JAVA 25 SELF /Sao chép và di chuyển tệp và thư mục

Sao chép và di chuyển tệp và thư mục

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Sao chép tệp

Phương thức Files.copy: cú pháp cơ bản

Trong Java, để sao chép tệp và thư mục, ta sử dụng phương thức tĩnh Files.copy từ gói java.nio.file. Chúng ta đã từng gặp nó, nhưng bây giờ sẽ tìm hiểu kỹ hơn. Cú pháp cơ bản của Files.copy:

Files.copy(Path source, Path target, CopyOption... options)
  • source — đường dẫn tới tệp hoặc thư mục nguồn.
  • target — đường dẫn đích để sao chép tới.
  • options — các tùy chọn bổ sung (ví dụ, cho phép ghi đè).

Ví dụ đơn giản nhất

Hãy sao chép tệp "input.txt" từ thư mục hiện tại vào thư mục "backup" với tên "input_backup.txt":

import java.nio.file.*;

public class CopyExample {
    public static void main(String[] args) throws Exception {
        Path source = Path.of("input.txt");
        Path target = Path.of("backup/input_backup.txt");

        Files.copy(source, target);
        System.out.println("Tệp đã được sao chép!");
    }
}

Điểm quan trọng:

  • Nếu tệp đích đã tồn tại, sẽ ném ngoại lệ FileAlreadyExistsException.
  • Nếu thư mục "backup" không tồn tại, phương thức sẽ ném lỗi — thư mục phải được tạo trước!

Sao chép có ghi đè

Để cho phép ghi đè tệp, hãy dùng tùy chọn StandardCopyOption.REPLACE_EXISTING:

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

Bảng: các tùy chọn sao chép chính

Tùy chọn Mô tả
StandardCopyOption.REPLACE_EXISTING
Ghi đè tệp nếu nó đã tồn tại
StandardCopyOption.COPY_ATTRIBUTES
Sao chép thuộc tính (ngày tạo, v.v.)
StandardCopyOption.ATOMIC_MOVE
Di chuyển nguyên tử (chỉ dùng với move)

Sao chép tệp sang thư mục khác

Trước khi sao chép tệp, hãy đảm bảo thư mục đích tồn tại:

Path targetDir = Path.of("backup");
if (!Files.exists(targetDir)) {
    Files.createDirectory(targetDir);
}
Files.copy(source, targetDir.resolve("input_backup.txt"), StandardCopyOption.REPLACE_EXISTING);

Sao chép thư mục: điểm cần lưu ý

Phương thức Files.copy có thể sao chép thư mục chỉ như một “vỏ rỗng” — nội dung (tệp và thư mục con) không được sao chép! Nếu bạn cố sao chép một thư mục có tệp bên trong, bạn sẽ chỉ nhận được một thư mục trống. Để sao chép toàn bộ thư mục kèm nội dung, cần duyệt đệ quy và sao chép thủ công từng tệp (chúng ta sẽ quay lại vấn đề này ở các bài giảng sau).

Ví dụ: sao chép tệp từ một thư mục sang thư mục khác

Path source = Path.of("data/report.txt");
Path target = Path.of("backup/report.txt");

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

Xử lý lỗi khi sao chép

Sao chép có thể thất bại vì nhiều lý do: không có quyền truy cập, tệp đang bận, thiếu dung lượng, tệp đã tồn tại, v.v. Tốt nhất luôn dùng try-catch:

try {
    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
    System.out.println("Sao chép thành công!");
} catch (FileAlreadyExistsException e) {
    System.out.println("Tệp đã tồn tại: " + target);
} catch (IOException e) {
    System.out.println("Lỗi I/O: " + e.getMessage());
}

2. Di chuyển tệp và thư mục

Phương thức Files.move: cú pháp

Việc di chuyển tệp và thư mục được thực hiện bằng phương thức:

Files.move(Path source, Path target, CopyOption... options)

Trong đó source — đường dẫn tới tệp (từ đâu di chuyển), target — nơi muốn đặt tệp hoặc thư mục, options — các thiết lập bổ sung (ví dụ, thay thế tệp đã tồn tại).

Nhân tiện, di chuyển không chỉ là “chuyển chỗ” tệp mà còn là cách để đổi tên (nếu chỉ thay đổi tên). Và nếu trong thư mục đích đã có tệp cùng tên, sẽ ném ngoại lệ nếu không chỉ định tùy chọn REPLACE_EXISTING.

Ví dụ: di chuyển tệp sang thư mục khác

Path source = Path.of("data/report.txt");
Path target = Path.of("archive/report.txt");

Files.move(source, target);
System.out.println("Tệp đã được di chuyển!");

Di chuyển và thay thế tệp hiện có

Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);

Di chuyển thư mục

Path oldDir = Path.of("data/old_reports");
Path newDir = Path.of("archive/old_reports");

Files.move(oldDir, newDir);

Toàn bộ thư mục, bao gồm các tệp và thư mục con bên trong, sẽ được chuyển đến vị trí mới.
Nhưng nếu trong thư mục đích đã có một thư mục cùng tên — bạn sẽ nhận lỗi.

Di chuyển giữa các hệ thống tệp khác nhau

Nếu bạn di chuyển tệp giữa các ổ đĩa khác nhau (ví dụ, từ "C:" sang "D:"), bên dưới sẽ thực hiện sao chép rồi xóa tệp nguồn. Nếu thao tác không được hỗ trợ theo kiểu nguyên tử, có thể ném ngoại lệ AtomicMoveNotSupportedException.

Sự khác nhau giữa movecopy

  • copy — tệp/thư mục gốc vẫn ở nguyên, xuất hiện một bản sao.
  • move — tệp/thư mục gốc biến mất, chỉ còn bản mới ở vị trí đích.

3. Đổi tên tệp và thư mục

Trong Java không có phương thức riêng “đổi tên tệp”. Đổi tên là một trường hợp đặc biệt của di chuyển: bạn chỉ định đường dẫn mới với tên khác trong cùng thư mục.

Ví dụ: đổi tên tệp

Giả sử có tệp "report.txt", và chúng ta muốn đổi tên nó thành "report_old.txt" trong cùng thư mục:

Path dir = Path.of("data");
Path oldName = dir.resolve("report.txt");
Path newName = dir.resolve("report_old.txt");

Files.move(oldName, newName);
System.out.println("Tệp đã được đổi tên!");

Những điểm quan trọng

Nếu trong thư mục đích đã có tệp hoặc thư mục với tên bạn chỉ định, phương thức sẽ ném ngoại lệ. Để vượt qua hạn chế này và cho phép thay thế, có thể dùng tùy chọn StandardCopyOption.REPLACE_EXISTING.

Ví dụ:

Path source = Paths.get("old_name.txt");
Path target = Paths.get("new_name.txt");

// Đổi tên tệp; nếu đã tồn tại — sẽ thay thế
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);

Trong ví dụ này, tệp "old_name.txt" sẽ được đổi tên thành "new_name.txt". Nếu trong thư mục đã có tệp cùng tên, nó sẽ bị ghi đè.

Đổi tên thư mục

Path oldDir = Path.of("data/old_reports");
Path newDir = Path.of("data/archived_reports");

Files.move(oldDir, newDir);
System.out.println("Thư mục đã được đổi tên!");

4. Xử lý lỗi khi sao chép, di chuyển và đổi tên

Làm việc với hệ thống tệp luôn tiềm ẩn rủi ro tình huống bất ngờ. Dưới đây là những vấn đề thường gặp nhất:

Tệp đã tồn tại.
Ngoại lệ: FileAlreadyExistsException. Cách xử lý: dùng REPLACE_EXISTING nếu muốn ghi đè.

Tệp hoặc thư mục đang bị tiến trình khác sử dụng.
Ngoại lệ: IOException. Ví dụ, tệp đang mở trong chương trình khác.

Không có quyền truy cập vào tệp hoặc thư mục.
Ngoại lệ: AccessDeniedException. Vấn đề về quyền của người dùng.

Di chuyển giữa các hệ thống tệp khác nhau.
Ngoại lệ: AtomicMoveNotSupportedException. Không phải mọi hệ thống tệp đều hỗ trợ di chuyển nguyên tử.

Thư mục đích không tồn tại.
Đối với sao chép/di chuyển, hãy đảm bảo thư mục đích được tạo sẵn!

Ví dụ xử lý lỗi

try {
    Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
} catch (FileAlreadyExistsException e) {
    System.out.println("Tệp đích đã tồn tại: " + target);
} catch (AtomicMoveNotSupportedException e) {
    System.out.println("Di chuyển nguyên tử không được hỗ trợ: " + e.getMessage());
} catch (IOException e) {
    System.out.println("Lỗi khi di chuyển: " + e.getMessage());
}

5. Khuyến nghị thực hành

Kiểm tra sự tồn tại của tệp/thư mục đích

Trước khi sao chép hoặc di chuyển, luôn hữu ích khi kiểm tra xem đường dẫn đích đã tồn tại hay chưa:

if (Files.exists(target)) {
    System.out.println("Tệp đã tồn tại, sẽ không ghi đè.");
} else {
    Files.copy(source, target);
}

Sử dụng tệp tạm để thao tác an toàn

Với các thao tác phức tạp (ví dụ, cập nhật tệp quan trọng), thường dùng tệp tạm — trước tiên sao chép vào tệp tạm, sau đó thay thế tệp chính:

Path temp = Path.of("data/report.tmp");
Files.copy(source, temp, StandardCopyOption.REPLACE_EXISTING);
Files.move(temp, target, StandardCopyOption.REPLACE_EXISTING);

Cách tiếp cận này giảm thiểu rủi ro mất dữ liệu khi xảy ra sự cố.

Ví dụ: sao lưu tệp trước khi thay thế

Path file = Path.of("data/report.txt");
Path backup = Path.of("data/report_backup.txt");

if (Files.exists(file)) {
    Files.copy(file, backup, StandardCopyOption.REPLACE_EXISTING);
    System.out.println("Đã tạo bản sao lưu.");
}

6. Bảng: các phương thức và tùy chọn chính

Thao tác Phương thức Java NIO Đặc điểm
Sao chép
Files.copy(source, target, ...)
Không sao chép nội dung của thư mục!
Di chuyển
Files.move(source, target, ...)
Có thể dùng để đổi tên
Đổi tên
Files.move(source, target, ...)
Đường dẫn mới trong cùng thư mục
Ghi đè
StandardCopyOption.REPLACE_EXISTING
Ghi đè tệp/thư mục đích
Kiểm tra
Files.exists(path)
Kiểm tra sự tồn tại của tệp/thư mục

7. Những lỗi thường gặp khi sao chép, di chuyển và đổi tên tệp

Lỗi số 1: cố gắng sao chép tệp vào thư mục không tồn tại. Nếu thư mục đích chưa được tạo, sẽ xảy ra NoSuchFileException. Cách xử lý: tạo thư mục trước bằng Files.createDirectories.

Lỗi số 2: cố gắng sao chép toàn bộ thư mục bằng Files.copy. Phương thức chỉ sao chép “vỏ rỗng”, không sao chép nội dung. Để sao chép đầy đủ cần duyệt đệ quy.

Lỗi số 3: không xử lý trường hợp tệp đã tồn tại. Nếu không truyền REPLACE_EXISTING, mà tệp đã có, sẽ ném ngoại lệ. Tốt hơn nên xử lý rõ ràng trường hợp này.

Lỗi số 4: cố gắng đổi tên khi tệp với tên mới đã tồn tại. Tương tự, sẽ gặp FileAlreadyExistsException. Trước khi đổi tên có thể kiểm tra sự tồn tại của tệp.

Lỗi số 5: di chuyển giữa các hệ thống tệp khác nhau. Đôi khi thao tác không được hỗ trợ “nguyên tử” và ném AtomicMoveNotSupportedException. Khi đó hãy dùng di chuyển thông thường không nguyên tử.

Lỗi số 6: thiếu quyền truy cập hoặc tệp bị khóa. Nếu tệp đang mở trong chương trình khác hoặc không có quyền đọc/ghi, bạn sẽ gặp AccessDeniedException hoặc IOException. Luôn xử lý các ngoại lệ này.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION