Xử Lý Tệp ZIP
(Handling ZIP files)

ZIP là một định dạng tệp nén phổ biến, viết tắt của "Zoned Improvement Plan" (kế hoạch cải thiện vùng), mặc dù trong ngữ cảnh nén tệp, nó không thực sự liên quan đến thuật ngữ này. ZIP được sử dụng để giảm kích thước của các tệp và thư mục bằng cách nén chúng lại, cho phép lưu trữ và truyền tải dễ dàng hơn. ZIP là một định dạng không mất dữ liệu, nghĩa là khi bạn giải nén, các tệp sẽ trở lại trạng thái ban đầu mà không bị mất mát thông tin.

Trong lập trình Java, việc xử lý các tệp nén ZIP là một kỹ năng quan trọng và hữu ích. ZIP là một định dạng tệp nén phổ biến được sử dụng rộng rãi để giảm kích thước của các tệp và thư mục, giúp dễ dàng hơn trong việc lưu trữ và truyền tải qua mạng. Java cung cấp một bộ công cụ mạnh mẽ để làm việc với các tệp ZIP thông qua các lớp thuộc gói java.util.zip.

Chương này sẽ hướng dẫn bạn cách tạo, đọc, và giải nén các tệp ZIP bằng Java. Bạn sẽ học cách sử dụng lớp ZipFile để thực hiện các tác vụ như nén nhiều tệp vào một tệp ZIP duy nhất, đọc nội dung của một tệp ZIP hiện có, và giải nén các tệp từ tệp ZIP. Bên cạnh đó, chương này cũng sẽ giới thiệu các phương pháp tối ưu để quản lý bộ nhớ và xử lý lỗi khi làm việc với các tệp ZIP.

Bằng cách nắm vững các kỹ thuật này, bạn sẽ có thể tích hợp khả năng nén và giải nén tệp vào các ứng dụng Java của mình, từ đó cải thiện hiệu suất và trải nghiệm người dùng.

Lớp ZipFile

Lớp ZipFile trong Java là một lớp dùng để đọc các mục (entries) từ một tệp ZIP. Nó mở rộng từ lớp Object và triển khai giao diện Closeable, điều này có nghĩa là bạn có thể đóng đối tượng ZipFile sau khi hoàn thành công việc, thường là bằng cách sử dụng câu lệnh try-with-resources để đảm bảo rằng tài nguyên được giải phóng đúng cách.

Một số điểm cần lưu ý khi sử dụng lớp ZipFile:

↳ Đọc tệp ZIP: Lớp này chủ yếu được sử dụng để đọc các tệp và thư mục nén bên trong tệp ZIP.

↳ Xử lý ngoại lệ NullPointerException: Nếu bạn truyền một đối số null cho bất kỳ phương thức hay constructor nào của lớp này, nó sẽ gây ra ngoại lệ NullPointerException.

Điều này giúp bạn tránh lỗi khi làm việc với tệp ZIP, đảm bảo rằng tất cả các đối số đều hợp lệ và tài nguyên được quản lý hiệu quả.

Khai báo lớp ZipFile trong Java

Để sử dụng lớp ZipFile, bạn cần import gói java.util.zip bạn cần thêm câu lệnh import vào đầu file Java của mình.

Cú pháp câu lệnh import:

Cú pháp

import java.util.zip.ZipFile;

Cú pháp khai báo lớp ZipFile:

Cú pháp

public class ZipFile
extends Object
implements Closeable

Dưới đây là giải thích chi tiết về cú pháp khai báo này:

↳ public: Từ khóa này xác định rằng lớp ZipFile có thể được truy cập từ bất kỳ đâu, không chỉ từ bên trong cùng một gói (package). Điều này có nghĩa là bất kỳ lớp nào trong ứng dụng, kể cả những lớp ở các gói khác nhau, đều có thể sử dụng lớp ZipFile.

↳ class: Từ khóa này cho biết rằng chúng ta đang khai báo một lớp.

↳ ZipFile: Đây là tên của lớp. Lớp này đại diện cho các tệp ZIP (tệp nén), cho phép đọc và truy cập nội dung của chúng.

↳ extends Object: Tất cả các lớp trong Java đều ngầm định kế thừa từ lớp Object nếu chúng không kế thừa từ lớp nào khác. Do đó, extends Object chỉ ra rằng ZipFile kế thừa từ lớp Object, mặc dù việc chỉ định này không bắt buộc vì nó mặc định.

↳ implements Closeable: ZipFile thực thi giao diện Closeable, có nghĩa là lớp này phải cung cấp một triển khai cho phương thức close() của giao diện Closeable. Giao diện Closeable thuộc về gói java.io và được thiết kế để đóng các tài nguyên như tệp hoặc kết nối mạng khi chúng không còn cần thiết, giúp quản lý tài nguyên một cách an toàn và hiệu quả.

Các trường trong lớp ZipFile

Các trường trong lớp ZipFile thường được sử dụng nội bộ để quản lý và xử lý thông tin trong tệp ZIP. Dưới đây là danh sách các trường cùng với mô tả ngắn gọn:

↳ static int CENATT: Thuộc tính tập trung (Central Directory File Header) của mục trong tệp ZIP.

↳ static int CENATX: Thuộc tính tập trung bổ sung của mục trong tệp ZIP.

↳ static int CENCOM: Độ dài của chuỗi nhận xét trong tập trung (Central Directory).

↳ static int CENCRC: CRC-32 checksum của mục trong tệp ZIP.

↳ static int CENDSK: Số đĩa nơi tập trung (Central Directory) bắt đầu.

↳ static int CENEXT: Độ dài của trường phần mở rộng trong tập trung (Central Directory).

↳ static int CENFLG: Cờ (flags) của mục trong tập trung (Central Directory).

↳ static int CENHDR: Độ dài tiêu đề tập trung (Central Directory Header).

↳ static int CENHOW: Phương pháp nén (compression method) được sử dụng cho mục trong tệp ZIP.

↳ static int CENLEN: Độ dài tệp sau khi nén trong tập trung (Central Directory).

↳ static int CENNAM: Độ dài tên tệp trong tập trung (Central Directory).

↳ static int CENOFF: Offset (độ lệch) của mục từ đầu tệp trong tập trung (Central Directory).

↳ static long CENSIG: Chữ ký (signature) của tiêu đề tập trung (Central Directory).

↳ static int CENSIZ: Kích thước nén của mục trong tập trung (Central Directory).

↳ static int CENTIM: Thời gian của mục trong tiêu đề tập trung (Central Directory). Thời gian này thường được lưu trữ dưới dạng số nguyên, đại diện cho giờ và phút của thời điểm mục được nén.

↳ static int CENVEM: Mã phiên bản của hệ thống nén (Compression System Version) trong tiêu đề tập trung (Central Directory). Mã này cho biết phiên bản của hệ thống nén đã được sử dụng để tạo ra mục này.

↳ static int CENVER: Phiên bản của định dạng nén (Compression Format Version) trong tiêu đề tập trung (Central Directory). Trường này lưu phiên bản của định dạng nén được sử dụng.

↳ static int ENDCOM: Độ dài của chuỗi nhận xét trong tiêu đề kết thúc (End of Central Directory). Trường này lưu trữ độ dài của các nhận xét, nếu có.

↳ static int ENDHDR: Độ dài của tiêu đề kết thúc (End of Central Directory Header). Trường này chứa kích thước của tiêu đề kết thúc.

↳ static int ENDOFF: Offset (độ lệch) của tiêu đề kết thúc (End of Central Directory) từ đầu tệp ZIP. Trường này giúp xác định vị trí của tiêu đề kết thúc trong tệp ZIP.

↳ static long ENDSIG: Chữ ký (signature) của tiêu đề kết thúc (End of Central Directory). Đây là giá trị đặc biệt dùng để xác thực tiêu đề kết thúc.

↳ static int ENDSIZ: Kích thước của tập hợp các mục trong tiêu đề kết thúc (End of Central Directory). Trường này cho biết tổng kích thước của các mục được lưu trong tiêu đề kết thúc.

↳ static int ENDSUB: Số lượng mục (entries) trong tiêu đề kết thúc (End of Central Directory). Trường này lưu số lượng mục trong tập hợp.

↳ static int ENDTOT: Tổng số mục (entries) trong toàn bộ tập tin ZIP. Trường này cho biết số lượng mục trong tất cả các đĩa.

↳ static int EXTCRC: CRC-32 checksum của phần mở rộng (Extended Header) trong tệp ZIP. Trường này dùng để xác thực phần mở rộng.

↳ static int EXTHDR: Độ dài tiêu đề phần mở rộng (Extended Header). Trường này lưu kích thước của tiêu đề phần mở rộng.

↳ static int EXTLEN: Độ dài của phần mở rộng trong tệp ZIP. Trường này chứa kích thước của phần mở rộng.

↳ static long EXTSIG: Chữ ký (signature) của phần mở rộng (Extended Header). Trường này dùng để xác thực phần mở rộng.

↳ static int EXTSIZ: Kích thước của phần mở rộng trong tệp ZIP. Trường này cho biết tổng kích thước của phần mở rộng.

↳ static int LOCCRC: CRC-32 checksum của dữ liệu trong mục địa phương (Local File Header). Trường này giúp xác thực tính toàn vẹn của dữ liệu nén.

↳ static int LOCEXT: Độ dài của phần mở rộng (Extended Header) trong mục địa phương. Trường này chứa kích thước của phần mở rộng nếu có.

↳ static int LOCFLG: Cờ (flag) của mục địa phương. Trường này lưu các cờ đặc biệt, như liệu mục có được nén hay không.

↳ static int LOCHDR: Độ dài của tiêu đề mục địa phương. Trường này cho biết kích thước của tiêu đề trong mục địa phương.

↳ static int LOCHOW: Phương pháp nén được sử dụng trong mục địa phương. Trường này lưu phương pháp nén được áp dụng cho dữ liệu.

↳ static int LOCLEN: Kích thước của dữ liệu nén trong mục địa phương. Trường này chứa kích thước dữ liệu đã nén.

↳ static int LOCNAM: Độ dài của tên tệp trong mục địa phương. Trường này lưu kích thước của tên tệp.

↳ static long LOCSIG: Chữ ký (signature) của tiêu đề mục địa phương. Đây là giá trị đặc biệt dùng để xác thực tiêu đề mục địa phương.

↳ static int LOCSIZ: Kích thước của dữ liệu không nén trong mục địa phương. Trường này cho biết kích thước của dữ liệu chưa được nén.

↳ static int LOCTIM: Thời gian của mục địa phương. Trường này lưu thời gian khi mục được nén.

↳ static int LOCVER: Phiên bản của định dạng nén trong mục địa phương. Trường này lưu phiên bản của định dạng nén được sử dụng.

↳ static int OPEN_DELETE: Cờ chế độ để mở tệp ZIP và đánh dấu nó để xóa. Sử dụng cờ này khi bạn muốn mở tệp và đánh dấu nó để xóa sau khi sử dụng.

↳ static int OPEN_READ: Cờ chế độ để mở tệp ZIP chỉ để đọc. Sử dụng cờ này khi bạn chỉ cần đọc nội dung của tệp ZIP mà không cần sửa đổi nó.

Các Constructor của lớp ZipFile

Lớp ZipFile có tổng cộng 6 constructor để mở một file ZIP.

↳ ZipFile(File file): Mở file ZIP dựa trên đối tượng File với mã hóa mặc định (UTF-8).

↳ ZipFile(File file, Charset charset): Mở file ZIP dựa trên đối tượng File với mã hóa ký tự chỉ định.

↳ ZipFile(File file, int mode): Mở file ZIP với chế độ mở cụ thể (ví dụ: OPEN_READ hoặc OPEN_DELETE).

↳ ZipFile(File file, int mode, Charset charset): Mở file ZIP với chế độ mở và mã hóa ký tự chỉ định.

↳ ZipFile(String name): Mở file ZIP dựa trên tên file với mã hóa mặc định (UTF-8).

↳ ZipFile(String name, Charset charset): Mở file ZIP dựa trên tên file với mã hóa ký tự chỉ định.

Các phương thức của lớp ZipFile

Lớp ZipFile cung cấp các phương thức để tương tác với một file ZIP đã tồn tại. Nó cho phép bạn đọc các mục trong file ZIP, lấy thông tin về file ZIP và đóng file khi hoàn tất. Dưới đây là danh sách tất cả các phương thức của lớp ZipFile trong Java:

↳ void close(): Đóng file ZIP và giải phóng tài nguyên.

↳ Enumeration<? extends ZipEntry> entries(): Trả về một Enumeration của các mục trong file ZIP.

↳ String getComment(): Trả về bình luận của file ZIP, hoặc null nếu không có.

↳ ZipEntry getEntry(String name): Trả về một ZipEntry đại diện cho mục có tên đã cho, hoặc null nếu không tìm thấy.

↳ InputStream getInputStream(ZipEntry entry): Trả về một InputStream để đọc nội dung của mục ZIP đã chỉ định.

↳ StringgetName(): Trả về đường dẫn của file ZIP.

↳ int size(): Trả về số lượng mục trong file ZIP.

↳ Stream<? extends ZipEntry> stream(): Trả về một Stream các mục trong file ZIP.

↳ protected void finalize(): Đảm bảo rằng các tài nguyên hệ thống mà đối tượng ZipFile đang giữ sẽ được giải phóng khi không còn tham chiếu nào đến đối tượng đó.

Ví dụ 1

Bạn có thể sử dụng lớp ZipFile trong Java để đọc các tệp bên trong một tệp ZIP:

Ví dụ: Example.java

import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class Example {
    public static void main(String[] args) {
        // Đường dẫn đến tệp ZIP
        String zipFilePath = "example.zip";

        // Sử dụng try-with-resources để tự động đóng tài nguyên
        try (ZipFile zipFile = new ZipFile(zipFilePath)) {
            // Lấy danh sách các mục (entry) trong tệp ZIP
            Enumeration<? extends ZipEntry> entries = zipFile.entries();

            // Duyệt qua từng mục trong tệp ZIP
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();

                // In tên của mục (tệp hoặc thư mục) ra màn hình
                System.out.println("Đã tìm thấy mục nhập: " + entry.getName());

                // Kiểm tra xem mục đó có phải là thư mục hay không
                if (entry.isDirectory()) {
                    System.out.println(entry.getName() + " là một thư mục.");
                } else {
                    System.out.println(entry.getName() + " là một thư tệp.");
                    // Bạn có thể đọc nội dung tệp bằng cách sử dụng zipFile.getInputStream(entry)
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Giả sử example.zip chứa các tệp sau:

|-- folder1/
|------|----- file1.txt
|-- file2.txt

Kết quả của chương trình là

Đã tìm thấy mục nhập: folder1/
folder1/ là một thư mục.
Đã tìm thấy mục nhập: folder1/file1.txt
folder1/file1.txt là một tệp.
Đã tìm thấy mục nhập: file2.txt
file2.txt là một tệp.

Lưu ý: Thay thế "example.zip" bằng đường dẫn thực tế đến tệp mà bạn muốn thao tác.

Ví dụ 2

Bạn có thể sử dụng ZipFile để giải nén các tệp từ một tệp ZIP và lưu chúng vào hệ thống tệp.

Ví dụ: Example.java

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class Example {
    public static void main(String[] args) {
        String zipFilePath = "example.zip";
        String destDir = "output_directory";

        File dir = new File(destDir);
        // Tạo thư mục đích nếu chưa tồn tại
        if (!dir.exists()) dir.mkdirs();

        try (ZipFile zipFile = new ZipFile(zipFilePath)) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                File file = new File(destDir, entry.getName());

                if (entry.isDirectory()) {
                    // Tạo thư mục
                    file.mkdirs();
                } else {
                    // Đảm bảo rằng thư mục chứa tệp đã tồn tại
                    new File(file.getParent()).mkdirs();

                    // Giải nén tệp
                    try (InputStream is = zipFile.getInputStream(entry);
                         FileOutputStream fos = new FileOutputStream(file)) {
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = is.read(buffer)) > 0) {
                            fos.write(buffer, 0, len);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Lưu ý: Thay thế "example.zip" và "output_directory" bằng đường dẫn thực tế đến tệp mà bạn muốn thao tác.

Ví dụ 3

Bạn có thể sử dụng ZipFile để xác định có bao nhiêu tệp trong một tệp ZIP:

Ví dụ: Example.java

import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.Enumeration;

public class Example {
    public static void main(String[] args) {
        String zipFilePath = "example.zip";
        int fileCount = 0;

        try (ZipFile zipFile = new ZipFile(zipFilePath)) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (!entry.isDirectory()) {
                    fileCount++;
                }
            }

            System.out.println("Tổng số tập tin: " + fileCount);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Lưu ý: Thay thế "example.zip" bằng đường dẫn thực tế đến tệp mà bạn muốn thao tác.

Hy vọng rằng qua các ví dụ trên, bạn sẽ hiểu rõ hơn về cách xử lý tệp ZIP trong Java.

Câu Nói Truyền Cảm Hứng

“Bắt đầu ở đâu không quan trọng, quan trọng là bạn sẵn sàng bắt đầu.” – W. Clement Stone

Không Gian Tích Cực

“Chúc bạn luôn giữ vững niềm tin và sức mạnh để vượt qua mọi thử thách trong cuộc sống.”