2011-11-21 9 views
10

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 @ResponseBodyBytArrayHttpMessageConverter - 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.

+0

Bạn có thể đặt return baos.toByteArray() sau} không? –

+0

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ờ :-) –

+0

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. –

Trả lời

2

Cố gắng cập nhật chú thích của mình để:

@RequestMapping(value = "thumbnail", method = { RequestMethod.GET }, produces = {"image/jpeg"})  
@ResponseBody 

Note produces thuộc tính.

Hy vọng điều đó sẽ hữu ích.

+0

điều này có thể hữu ích nếu chỉ thuộc tính sản xuất của yêu cầu ánh xạ được hỗ trợ trong phiên bản của mùa xuân tôi đang sử dụng. Tôi đang sử dụng Spring 3.0.6.RELEASE Điều này đã được thêm vào trong 3.1 M2 Cảm ơn gợi ý mặc dù. –

+1

Điều này không giải thích được các triệu chứng mà nó hoạt động bên trong '' nhưng không phải bên trong một tab/cửa sổ mới. – BalusC

1

Tôi vừa giải quyết được sự cố tương tự để có thể bạn (hoặc người khác) có thể tận dụng nó. Tôi đã biểu tượng được lưu trữ trong cơ sở dữ liệu psql:

create table images (image_id int not null primary key, data bytea) 

phương pháp của tôi trong bộ điều khiển là:

@RequestMapping(value = IMAGE + "/{imageId}", method = GET) 
    @ResponseBody 
    public ResponseEntity<byte[]> getImage(@PathVariable final Integer imageId) throws IOException { 
    Image img = imageService.getImage(imageId); 
    HttpHeaders responseHeaders = new HttpHeaders(); 
    responseHeaders.setContentType(MediaType.IMAGE_PNG); 
    responseHeaders.set("Content-Disposition", "attachment"); 
    return new ResponseEntity<byte[]>(img.getData(), responseHeaders, HttpStatus.OK); 
} 

Và kể từ khi tôi đang sử dụng thymeleaf html của tôi trông như thế này:

<img src="#" th:src="@{${navigator.IMAGE} + '/-1'}" /> 

tôi đặt imageId = -1 trong liên kết chỉ để thử nghiệm. Nó hoạt động trên FF.

Hy vọng nó sẽ giúp ai đó :)

+0

điều này có vẻ tốt. tôi sẽ thử lại sau. –