CodeGym /Các khóa học /JAVA 25 SELF /Tải ảnh từ Internet

Tải ảnh từ Internet

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

1. Giới thiệu

Làm việc với tệp và mạng là một trong những chủ đề quan trọng nhất trong lập trình. Hầu như mọi ứng dụng hiện đại đều tương tác với Internet theo cách này hay cách khác: tải dữ liệu, gửi yêu cầu, nhận hình ảnh hoặc tài liệu. Ngay cả một chương trình đơn giản như khách hàng chat hay trình phát nhạc cũng chắc chắn phải xử lý việc tải tài nguyên.

Hôm nay chúng ta sẽ tìm hiểu cách làm điều đó trong Java, lấy ví dụ trực quan và dễ chịu nhất — ảnh. Tại sao lại là ảnh?

  • Thấy ngay lập tức: Nếu mọi thứ hoạt động, bạn chỉ cần mở tệp đã tải và thấy hình ảnh. Đây là kết quả tức thì và dễ hiểu.
  • Kỹ năng phổ quát: Ảnh không chỉ là ảnh, mà là dữ liệu nhị phân, “byte thô”. Khi học cách làm việc với chúng, bạn có thể tải bất cứ thứ gì: tài liệu, video, nhạc, lưu trữ. Nguyên tắc là như nhau!
  • Bài toán thực tế: Khả năng tải tài nguyên từ mạng là nền tảng để tạo ra mọi ứng dụng hiện đại, từ các trình tải hình nền đơn giản đến các hệ thống phức tạp làm việc với API mạng xã hội.

Chúng ta sẽ hiểu bằng hai cách tiếp cận: “dịch vụ bưu chính” nhanh — qua URL, và “công ty vận tải chuyên nghiệp” — qua HttpClient với kiểm soát header, status và timeout.

2. Cách đơn giản nhất: lớp URL

Trong Java có lớp URL, đại diện cho một địa chỉ mạng. Nó cho phép mở kết nối và nhận một luồng dữ liệu (kiểu InputStream). Với luồng này, bạn có thể làm việc giống như với tệp: đọc byte, sao chép sang luồng khác, xử lý như văn bản.

Về bản chất, sử dụng URL là con đường ngắn nhất từ liên kết trên trình duyệt đến tệp trên đĩa.

Ví dụ tối thiểu đầu tiên

Giả sử chúng ta có một liên kết đến ảnh:

URL url = new URL("https://example.com/image.jpg");
Files.copy(url.openStream(), Path.of("a.jpg"));

Chỉ hai dòng. Điều gì đang xảy ra ở đây?

  1. Tạo đối tượng URL, truyền vào chuỗi địa chỉ của ảnh.
  2. Gọi phương thức openStream(), mở kết nối mạng và trả về luồng InputStream.
  3. Dùng Files.copy để sao chép nội dung của luồng vào tệp "a.jpg".

Kết quả: ảnh từ Internet được lưu vào thư mục làm việc của chúng ta.

Biến thể với transferTo

Có một cách khác để chuyển dữ liệu từ luồng sang tệp:

InputStream in = new URL("https://example.com/image.jpg").openStream();
in.transferTo(Files.newOutputStream(Path.of("b.jpg")));

Ở đây chúng ta nói rõ: lấy luồng in và “chuyển” toàn bộ dữ liệu bằng phương thức transferTo sang luồng ghi ra tệp "b.jpg".

Kết quả sẽ giống nhau: ảnh sẽ xuất hiện trên đĩa.

Các chi tiết quan trọng

Cách tiếp cận này rất đơn giản, nhưng có những điểm cần lưu ý. Thứ nhất, chúng ta không kiểm tra xem thực sự nhận được gì từ liên kết. Có thể đó là ảnh. Nhưng cũng có thể đó là trang HTML báo lỗi 404. Trong cả hai trường hợp, tệp vẫn được lưu. Sự khác biệt chỉ thấy sau này, khi bạn thử mở nó bằng trình xem ảnh.

3. Cách hiện đại: HttpClient

Bắt đầu từ Java 11, thư viện chuẩn có thêm công cụ mới — HttpClient. Nó cho phép thực hiện các yêu cầu HTTP ở mức hiện đại: làm việc với các phương thức GET, POST và khác, quản lý timeout, theo dõi header và status, xử lý chuyển hướng.

Đối với việc tải ảnh, điều này đặc biệt hữu ích: chúng ta có thể đảm bảo rằng tệp thực sự được tải thành công, chứ không phải là một lỗi.

Ví dụ tối thiểu với HttpClient

URI uri = URI.create("https://example.com/image.jpg");   // URI - phiên bản hiện đại hơn của URL

HttpClient client = HttpClient.newHttpClient();          // Tạo đối tượng HttpClient
HttpRequest request = HttpRequest.newBuilder(uri).build(); // Tạo đối tượng "yêu cầu"

HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("c.jpg"), response.body());

Bây giờ theo từng bước:

  1. Tạo client HttpClient.
  2. Xây dựng request HttpRequest tới địa chỉ đã chỉ định.
  3. Gửi request và nhận response dưới dạng mảng byte: HttpResponse.BodyHandlers.ofByteArray().
  4. Ghi các byte này ra tệp bằng Files.write"c.jpg".

Kết quả — vẫn là bức ảnh đó, nhưng giờ chúng ta có nhiều thông tin hơn về cách máy chủ phản hồi.

Kiểm tra mã trạng thái phản hồi

Đối tượng response có phương thức statusCode(). Với nó, ta có thể đảm bảo máy chủ trả về phản hồi thành công:

if (response.statusCode() == 200)
{
    Files.write(Path.of("ok.jpg"), response.body());
}
else
{
    System.out.println("Lỗi: mã " + response.statusCode());
}

Bây giờ chúng ta biết chắc rằng đã lưu đúng thứ mình mong đợi, chứ không phải một trang lỗi.

Lấy header

Giả sử ta muốn kiểm tra kiểu nội dung:

String type = response.headers().firstValue("Content-Type").orElse("không rõ");
System.out.println("Kiểu nội dung: " + type);

Nếu máy chủ trả về "image/png" hoặc "image/jpeg", thì đó thực sự là ảnh. Nếu nhận "text/html", đó là tín hiệu đáng ngờ.

Timeout

Để chương trình không bị treo nếu máy chủ phản hồi chậm, có thể đặt giới hạn:

URI uri = URI.create("https://example.com/image.jpg");
HttpRequest req = HttpRequest.newBuilder(uri)
    .timeout(Duration.ofSeconds(5))
    .build();

Nếu máy chủ không phản hồi trong 5 giây, yêu cầu sẽ kết thúc với lỗi.

Xử lý chuyển hướng

Đôi khi liên kết không trỏ trực tiếp đến tệp mà trước tiên dẫn đến một trang chuyển hướng. Bật tự động theo chuyển hướng như sau:

HttpClient client = HttpClient.newBuilder()
    .followRedirects(HttpClient.Redirect.NORMAL)
    .build();

4. Kịch bản thực tế

Tải nhiều ảnh liên tiếp

Thường cần tải không chỉ một mà hàng chục hoặc hàng trăm ảnh. Ví dụ, bạn viết chương trình lưu avatar người dùng hoặc ảnh sản phẩm. Với HttpClient, việc này được thực hiện bằng vòng lặp:

var client = HttpClient.newHttpClient();

String[] urls = {
    "https://example.com/img1.jpg",
    "https://example.com/img2.jpg"
};

for (int i = 0; i < urls.length; i++)
{
    var uri = URI.create(urls[i]);
    var request = HttpRequest.newBuilder(uri).build();
    var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
    if (response.statusCode() == 200) {
        Files.write(Path.of("img" + i + ".jpg"), response.body());
    }
}

Kiểm tra kích thước tệp

Đôi khi hữu ích khi biết ảnh có bao nhiêu byte. Máy chủ có thể thông báo điều này trong header "Content-Length":

String length = response.headers().firstValue("Content-Length").orElse("?");
System.out.println("Kích thước: " + length + " byte");

Nếu không có header, bạn luôn có thể lấy response.body().length.

Tải và hiển thị ảnh trong chương trình

Đôi khi cần không chỉ tải mà còn hiển thị ngay ảnh. Có thể dùng thư viện javax.imageio.ImageIO:

InputStream in = new URL("https://example.com/pic.png").openStream();
BufferedImage img = ImageIO.read(in);
System.out.println("Chiều rộng: " + img.getWidth() + ", chiều cao: " + img.getHeight());

Như vậy, ta có thể kiểm tra ảnh ngay trong bộ nhớ mà không cần lưu xuống đĩa.

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