Браузер отображает данные необработанного изображения jpeg. Какие заголовки я должен удостовериться в ответе?

Кажется, я столкнулся с интересной проблемой, когда браузер с радостью отображает изображение, сгенерированное моим веб-приложением Spring MVC, пока URL-адрес моего контроллера установлен как SRC тега IMG, но отображает двоичные данные, когда перейдите непосредственно к URL-адресу.

My Spring MVC Controller генерирует некоторый BufferedImage (миниатюра), преобразует его в byte[] и возвращает его непосредственно в тело ответа с помощью аннотации @ResponseBody в методе контроллера. Я зарегистрировал конвертер сообщений org.springframework.http.converter.ByteArrayHttpMessageConverter с AnnotationMethodHandlerAdapter и даже установил его свойство supportedMediaTypes на image/jpeg, что на самом деле не помогло, поэтому я устанавливаю заголовок Content-Type в ответе вручную в контроллере метод.

<img src="/images/thumbnail?id=1234" />

отлично работает и отображает изображение, однако переход непосредственно к SRC изображения (или щелчок правой кнопкой мыши по изображению и выбор вида изображения) заканчивается отображением необработанных данных изображения.

Заголовки ответов в соответствии с Firebug, полученные от запроса к такому URL-адресу (http://localhost: 8888/images/thumbnail? id = F0snPkvwhtDbl8eutbuq):

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)

Последнее слово: в Firebug, нажав на вкладку Response, вы увидите изображение:-) Что я упускаю? Я думал, что браузер получает заголовки контента и заголовков контента, знает, как ожидать jpeg-изображение, получает необработанные данные для jpeg, а затем отображает jpeg на пустой вкладке браузера. Как-то FF и Chrome отображают необработанные данные изображения.

Код, который я использую:

@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();
    }
}

=== UPDATE === Я последовал совету @BalusC и изменил URL-адрес, который создает миниатюру, чтобы выглядеть как фактический файл .jpg. Это в определенной степени повлияло на то, что теперь я могу "Сохранить изображение как", и имя файла больше не просто "thumbnail", а ".jpg", что хорошо. Тем не менее, как Chrome, так и FF (я даже не начал тестировать в IE) отображают необработанные данные JFIF при загрузке URL в новую вкладку/окно. Тем не менее, изображение отображается только в том случае, если URL-адрес находится в атрибуте SRC тега IMG и (благодаря кешированию браузера), когда пользователь выбирает View image на новой вкладке (но только если пользователь не обновляет вкладку, обновление вкладки будет повторно выбрано JPEG и отобразить необработанные данные в окне).

ИЗМЕНИТЬ Я только что протестировал это в IE9, и это единственный браузер, где он работает так, как ожидалось. Я могу перейти непосредственно к URL-адресу и продолжить обновление страницы, и я вижу, что мой контроллер попал, а JPEG загружен в окне браузера. Отлично. Теперь, чтобы понять, что не так с тем, как FF/CR обрабатывает JPEG, который я отправляю.

Дополнительная информация Я использую Spring версию 3.0.6.RELEASE Запуск веб-приложения из Jetty

ИЗМЕНИТЬ Я решил свою проблему с помощью не с помощью @ResponseBody и BytArrayHttpMessageConverter - я попробовал обходное решение, предложенное в другом потоке здесь, на SO -, чтобы просто написать байты непосредственно в выходной поток ответа: IOUtils.copy(imageInputStream, response.getOutputStream()); Это прямолинейно и работает, мне все же любопытно, какая была странная проблема с тем, как браузеры будут загружать ответ в теге <img>, но не непосредственно в окне браузера. Любой может пролить свет на это, мне было бы очень интересно узнать больше. Я оставляю этот вопрос без ответа.

Ответ 1

Попробуйте обновить аннотацию до:

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

Примечание produces.

Надеюсь, что это поможет.

Ответ 2

Я только что решил свою проблему, которая схожа, поэтому, возможно, вы (или кто-то другой) можете ее использовать. У меня есть значки, хранящиеся в базе данных psql:

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

Мой метод в контроллере:

    @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);
}

И поскольку я использую thymeleaf, мой html выглядит следующим образом:

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

Поместите imageId = -1 в ссылку только для тестирования. Он работает на FF.

Надеюсь, это поможет кому-то:)