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ả |
|---|---|
|
Ghi đè tệp nếu nó đã tồn tại |
|
Sao chép thuộc tính (ngày tạo, v.v.) |
|
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 move và copy
- 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 | |
Không sao chép nội dung của thư mục! |
| Di chuyển | |
Có thể dùng để đổi tên |
| Đổi tên | |
Đường dẫn mới trong cùng thư mục |
| Ghi đè | |
Ghi đè tệp/thư mục đích |
| Kiểm tra | |
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.
GO TO FULL VERSION