Tôi dường như đã gặp phải sự cố thú vị khi trình duyệt vui vẻ hiển thị hình ảnh được tạo bởi ứng dụng web MVC của tôi miễn là URL cho bộ điều khiển của tôi được đặt làm SRC của thẻ IMG, nhưng hiển thị dữ liệu nhị phân khi điều hướng trực tiếp đến URL.Trình duyệt hiển thị dữ liệu hình ảnh thô của jpeg. Tôi nên đảm bảo tiêu đề nào trong phản hồi?
Bộ điều khiển MVC Spring của tôi tạo ra một số BufferedImage
(hình thu nhỏ), chuyển đổi thành byte[]
và trả trực tiếp vào phần trả lời bằng cách sử dụng chú thích @ResponseBody
trên phương pháp bộ điều khiển. Tôi đã đăng ký bộ chuyển đổi tin nhắn org.springframework.http.converter.ByteArrayHttpMessageConverter
với số AnnotationMethodHandlerAdapter
và thậm chí đặt thuộc tính supportedMediaTypes
thành image/jpeg
, điều này không thực sự hữu ích, vì vậy tôi đặt tiêu đề Kiểu nội dung dựa trên phản hồi theo cách thủ công trong phương pháp điều khiển.
<img src="/images/thumbnail?id=1234" />
hoạt động tốt và hiển thị hình ảnh, tuy nhiên điều hướng trực tiếp đến SRC của hình ảnh (hoặc nhấp chuột phải và chọn Xem hình ảnh) sẽ hiển thị dữ liệu thô của hình ảnh.
tiêu đề đáp ứng, theo Firebug, nhận được từ một yêu cầu đến các URL tương tự (http: // localhost: 8888/images/thumbnail id = F0snPkvwhtDbl8eutbuq) là:
HTTP/1.1 200 OK
Expires: Wed, 21 Dec 2011 12:39:07 GMT
Cache-Control: max-age=2592000
Content-Type: image/jpeg
Content-Length: 6998
Server: Jetty(6.1.10)
Một lời cuối cùng: trong Firebug, nhấp vào tab Phản hồi hiển thị hình ảnh :-) Tôi đang bỏ lỡ điều gì? Tôi nghĩ rằng trình duyệt sẽ nhận được các tiêu đề nội dung kiểu và độ dài nội dung, biết mong đợi hình ảnh jpeg, nhận dữ liệu thô cho jpeg, sau đó hiển thị jpeg trong tab trình duyệt trống. Bằng cách nào đó FF và Chrome đang hiển thị dữ liệu hình ảnh thô đã nhận được.
Mã Tôi đang sử dụng:
@RequestMapping(value = "thumbnail", method = { RequestMethod.GET })
@ResponseBody
public byte[] getImageThumbnail(@RequestParam("id") String documentId, HttpServletResponse response) {
try {
Document document = documentService.getDocumentById(documentId);
InputStream imageInputStream = new FileInputStream(document.getUri());
response.setContentType("image/jpeg");
BufferedImage img = ImageIO.read(imageInputStream);
ResampleOp resampleOp = new ResampleOp(THUMBNAIL_DIMENSION);
BufferedImage thumbnail = resampleOp.filter(img, null);
return getDataFromBufferedImage(thumbnail);
} catch (Throwable t) {
return null; //return no data if document not found or whatever other issues are encountered
}
}
private byte[] getDataFromBufferedImage(BufferedImage thumbnail) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(thumbnail, "jpg", baos);
baos.flush();
return baos.toByteArray();
} finally {
baos.close();
}
}
=== CẬP NHẬT === Tôi đã theo lời khuyên @ BalusC và thay đổi url tạo hình thu nhỏ để trông giống như một file .jpg thực tế. Điều này đã làm cho một số khác biệt trong đó bây giờ tôi có thể "Lưu hình ảnh như" và tên tập tin không còn chỉ là "hình thu nhỏ" nhưng ".jpg" đó là tốt. Tuy nhiên, cả Chrome và FF (tôi thậm chí chưa bắt đầu thử nghiệm trên IE) hiển thị dữ liệu JFIF thô khi tải URL vào một tab/cửa sổ mới. Tuy nhiên, hình ảnh chỉ hiển thị nếu URL nằm trong thuộc tính SRC của thẻ IMG và (nhờ vào bộ nhớ đệm của trình duyệt) khi người dùng chọn Xem hình ảnh trong tab mới (nhưng chỉ khi người dùng không làm mới tab, làm mới tab sẽ tìm nạp lại JPEG và hiển thị dữ liệu thô trong cửa sổ).
EDIT Tôi vừa thử nghiệm điều này trong IE9 và đây là trình duyệt duy nhất hoạt động như mong đợi. Tôi có thể điều hướng đến URL trực tiếp và tiếp tục làm mới trang và tôi có thể thấy bộ điều khiển của tôi đang được nhấn và JPEG được tải trong cửa sổ trình duyệt. Xuất sắc. Bây giờ để tìm ra những gì là sai với cách FF/CR xử lý JPEG tôi đang gửi.
Thông tin bổ sung Tôi đang sử dụng phiên bản Spring 3.0.6.RELEASE Chạy ứng dụng web từ Jetty
EDIT Tôi đã giải quyết vấn đề của tôi bởi không sử dụng @ResponseBody
và BytArrayHttpMessageConverter
- Tôi đã thử các cách giải quyết đề nghị trong chủ đề khác ở đây trên SO - đó là chỉ cần viết các byte trực tiếp vào Luồng đầu ra phản hồi: IOUtils.copy(imageInputStream, response.getOutputStream());
Điều này đơn giản và hiệu quả, tôi vẫn tò mò vấn đề lạ lùng với cách trình duyệt sẽ tải phản hồi trong thẻ <img>
nhưng không trực tiếp trong cửa sổ trình duyệt. Bất cứ ai cũng có thể làm sáng tỏ điều này, tôi thực sự tò mò muốn tìm hiểu thêm. Tôi để lại câu hỏi này chưa được trả lời.
Bạn có thể đặt return baos.toByteArray() sau} không? –
ok, đã làm điều đó. đã không giải quyết vấn đề của tôi nhưng trông tốt hơn bây giờ :-) –
Vẫn ít có khả năng hơn, nhưng ImageIO.write với "jpeg" tức là "jpg" dường như được dự định trong API. Nên làm việc cả hai cách. Có thể các byte đánh dấu khác nhau. –