как мне отобразить картинку из базы данных используя freemaker?
Я собираюсь деплоить приложение на heroku и для меня легкий выход это хранить изображения в БД. Но у меня не получается отобразить картинку, которую я сохраняю в БД.
Одна из ошибок которая отображется:
freemarker.core.NonStringOrTemplateOutputException: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a sequence (wrapper: f.t.SimpleSequence):
==> message.img [in template "main.ftlh" at line 69, column 89]
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${message.img} [in template "main.ftlh" at line 69, column 87]
~ Reached through: #nested [in template "parts/common.ftlh" in macro "page" at line 17, column 5]
~ Reached through: @c.page [in template "main.ftlh" at line 3, column 1]
----
так я пытаюсь отобразить картинку в main.ftl:
<#if message.img??>
<img class="card-img-top" src="/img/${message.img}" alt="Card image cap" style="width:100%">
</#if>
Entity выглядит следующим образом:
@Entity
public class Message {
...код...
@Lob
private byte[] img;
...код...
}
В контролере я получаю файл с помощью:
@PostMapping("/main")
public String add(
@AuthenticationPrincipal User user,
@Valid Message message,
BindingResult bindingResult,
Model model,
@RequestParam("file")MultipartFile file
) throws IOException {
message.setAuthor(user);
if(bindingResult.hasErrors()) {
Map<String, String> errorsMap = ControllerUtils.getErrors(bindingResult);
model.addAttribute("message", message);
model.mergeAttributes(errorsMap);
} else {
if (file != null && !file.getOriginalFilename().isEmpty()) {
message.setImg(file.getBytes());
}
model.addAttribute("message", null);
messageRepo.save(message);
}
Iterable<Message> messages = messageRepo.findAll();
model.addAttribute("messages",messages);
return "main";
}
Прошу помощи и указать на ошибки.
Понимаю, что это путь src="/img/${message.img}", но не знаю, как отобразить картинку напрямую из БД. Как мне лучше сделать
Ответы (2 шт):
В чем проблема?
Атрибут src тега <img/> предназначен для того, чтобы указывать в нём ссылку на изображение.
Это значит, что браузер может получить строку из этого атрибута и используя данную строку как адрес, браузер может отправить HTTP запрос, на который ожидает получить 200-ый HTTP ответ с содержимым картинки в теле ответа.
Нельзя просто взять и указать
src="/img/${message.img}"
и надеяться на то, что приложение само за вас настроит маршрутизацию и отдачу контента с сервера.
Вы просто пытаетесь вставить массив байтов в верстку.
Эти данные будут в лучшем случае преобразованы в строку.
Как будет выглядеть подобная строка, вы можете поинтересоваться открыв любое изображение в блокноте.
Спойлер: нет!
судя по исключению FreeMaker не смог автоматически конвертировать массив байтов в строку
Expected a string or something automatically convertible to string (number, date or boolean)
Что делать?
1. Нужно реализовать отдачу изображений
Для этого добавьте в контроллер метод, который получает из БД сообщение и возвращает его содержимое в виде HTTP-ответа.
Т.к. нам нужно будет искать изображения в БД, было бы неплохо как-то получать идентификатор записи из запроса.
Для этого:
- укажите в шаблоне пути плейсхолдер
{id} - для перехвата значения из шаблона пути объявите параметр
Long idи пометьте его аннотацией@PathVariable("id")для того чтобы связать его с плейсхолдером - пометьте метод контроллера аннотацией
@ResponseBody, чтобы использовать возвращаемое методом значение в качестве тела HTTP ответа - для того чтобы спринг сформировал корректные HTTP-заголовки, соответствующие типу отдаваемого контента, укажите в аннотации
@GetMappingатрибутproducesсо значениемMediaType.IMAGE_PNG_VALUEдля PNG илиMediaType.IMAGE_JPEG_VALUEдля JPEG изображений - с помощью репозитория получите из БД запись по идентификатору
- обязательно проверьте наличие записи в результатах поиска и наличие содержимого изображения в записи
- верните содержимое изображения, в противном случае выкиньте исключение с 404 ошибкой
@ResponseBody
@GetMapping(value = "/img/messages/{id}.png", produces = MediaType.IMAGE_PNG_VALUE)
public byte[] img(@PathVariable("id") Long id, HttpServletRequest request) {
Optional<Message> messageSearchResult = messageRepo.findById(id);
if(messageSearchResult.isPresent()) {
Message message = messageSearchResult.get();
if (message.getImg() != null && message.getImg().length > 0){
return message.getImg();
}
}
// если выполнение дошло до данной строки, значит ничего не было найдено
// выбрасываем 404
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Image Not Found");
}
Формируем и указываем в шаблоне корректную ссылку на изображение
Далее указываем в шаблоне ссылку, соответствующую шаблону пути из вышеуказанного метода и пробрасываем в него идентификатор сообщения
<#if message.img??>
<img class="card-img-top" src="/img/messages/${message.id}.png" alt="Card image cap" style="width:100%">
</#if>
В контролере метод чуть отредоктирован:
@ResponseBody
@GetMapping(value = "/img/messages/{name}", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] img(@PathVariable("name") String name, HttpServletResponse response) throws IOException {
Optional<Message> messageSearchResult = messageRepo.findByFilename(name);
if(messageSearchResult.isPresent()) {
Message message = messageSearchResult.get();
if (message.getImg() != null && message.getImg().length > 0){
byte[] img = message.getImg();
response.setContentType("image/jpeg");
response.setContentLength(img.length);
response.getOutputStream().write(img);
return message.getImg();
}
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Image Not Found");
}
Во view
<#if message.filename??>
<img class="card-img-top" src="img/messages/${message.filename}" alt="Card image cap" style="width:100%">
</#if>
Ну и еще в application.properies добавить нужно было, чтобы картинка загружалась:
spring.datasource.hikari.auto-commit=false