【SpringBoot×MySQL】DBに登録した画像を表示したい
解決したいこと
現在書籍の管理アプリを作成しており、HTMLにて画像データを登録し、SpringBootを利用して処理し、MySQLに保存した画像データを再度HTMLに表示させたいのですが、表示できず苦戦しています。
流れとしては下記の通りです。
発生しているエラー
Google Chromeのデベロッパーツールで確認すると、画像のパスは渡せています。
画像の格納方法や表示の仕方に問題があるのかなと考えているのですが、解決方法を教えていただきたいです。
<!-- 書籍の一覧表示 -->
<div class="container">
<div class="row">
<div class="col">
<div>テスト1</div>
<img scr="/Users/manami/workspace/yonda/books/hanako.png" class="img-responsive img-rounded item-img-center" width="200">
</div>
</div>
</div>
使用しているツール
・Mac 12.1
・SpringBoot 4.12.1
ー Gradle
ー MyBatis
・MySQL 5.7.36
実際のコード(冗長になるので一部抜粋しています)
book-registraiton.html(書籍登録フォーム)
<h2>書籍登録</h2>
<form th:action="@{/yonda/book-registration-result}" method="post" enctype="multipart/form-data" th:object="${bookRegistrationForm}">
<label for="bookImg" class="text-muted">イメージ画像</label>
<input id="bookImg" type="file" accept="image/*" class="form-control" name="bookImg" th:field="*{bookImg}" required>
</div>
<button type="submit">登録</button>
</form>
BookRegistrationForm.java
// フィールド
private MultipartFile bookImg;
// getter/setter
public MultipartFile getBookImg() {
return bookImg;
}
public void setBookImg(MultipartFile bookImg) {
this.bookImg = bookImg;
}
BookRegistrationController.java(書籍登録を行うコントローラー)
@RequestMapping("/book-registration-result")
public String registerBook(@Validated BookRegistrationForm form, BindingResult result, MultipartFile bookImg, Model model) throws IOException {
// バリデーションチェック
if (result.hasErrors()) {
return registrationBook(model);
}
// 登録された画像データからファイル名を取得
String bookImgName = form.getBookImg().getOriginalFilename();
File filePath = new File(bookImg.getOriginalFilename());
// 保存先を定義
String uploadPath = "/Users/manami/workspace/yonda/books/";
// アップロードファイルをバイト値に変換
byte[] bytes = form.getBookImg().getBytes();
// バイト値を書き込むためのファイルを作成して指定したパスに格納
BufferedOutputStream stream = new BufferedOutputStream (
new FileOutputStream(new File(uploadPath + filePath)));
// ファイルに書き込み
stream.write(bytes);
Book book = new Book();
// formのデータをbookに書き込み
BeanUtils.copyProperties(form, book);
// sessionに格納されたユーザーデータを利用し、ユーザーのID情報を取得する
User user = (User) session.getAttribute("user");
book.setBookUserId(user.getUserId());
// 画像名とデータをドメインに渡す
book.setBookImgName(bookImgName);
book.setBookImg(Base64.encodeBase64(form.getBookImg().getBytes()));
// 書籍を登録する
bookRegistrationService.insertBook(book);
return "book-list";
}
BookRegistrationService.java(書籍登録処理を実装)
public void insertBook(Book book) {
bookMapper.insertBook(book);
}
BookMapper.java
@Mapper
public interface BookMapper {
/** 書籍登録 */
public void insertBook(Book book);
/** 書籍情報の全件取得(全ユーザーの書籍情報) */
public List<Book> findAll();
/** 特定ユーザーの書籍情報を全件取得 */
public List<Book> findByOne(@Param("bookUserId") Integer bookUserId);
/** 特定のユーザーの中から書籍idが一致する書籍情報の取得 */
public Book findById(@Param("bookUserId") Integer bookUserId, @Param("bookId") Integer bookId);
/** 書籍名が一致する情報の取得 */
public List<Book> findByName(@Param("bookUserId") Integer bookUserId, @Param("bookName") String bookName);
/** 書籍情報の更新 */
public void updateBook(Book book);
}
BookMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.repository.BookMapper">
<resultMap type="com.example.demo.domain.Book" id="bookMapper">
<id property="bookId" column="book_id"/>
<result property="bookName" column="book_name"/>
<result property="bookCategoryId" column="book_category_id"/>
<result property="bookPrice" column="book_price"/>
<result property="bookPage" column="book_page"/>
<result property="bookCurrentPage" column="book_current_page"/>
<result property="bookImgName" column="book_img_name"/>
<result property="bookImg" column="book_img" typeHandler="org.apache.ibatis.type.BlobTypeHandler"/>
<result property="bookUserId" column="book_user_id"/>
<result property="createAt" column="create_at"/>
<result property="updateAt" column="update_at"/>
</resultMap>
<!-- 書籍登録 -->
<insert id="insertBook">
INSERT INTO books (
book_name,
book_category_id,
book_price,
book_page,
book_img_name,
book_img,
book_user_id)
VALUES (
#{bookName},
#{bookCategoryId},
#{bookPrice},
#{bookPage},
#{bookImgName},
#{bookImg},
#{bookUserId});
</insert>
<!-- 書籍情報の全件取得(全ユーザーの書籍情報) -->
<select id="findAll" resultMap="bookMapper">
SELECT
b.book_id,
b.book_name,
b.book_price,
b.book_page,
b.book_current_page,
b.book_img_name,
b.book_img,
b.book_category_id,
c.category_name,
b.book_user_id,
u.user_name,
b.create_at,
b.update_at
FROM
books AS b
JOIN
categories AS c
ON
b.book_category_id = c.category_id
JOIN
users AS u
ON
b.book_user_id = u.user_id
ORDER BY
book_id;
</select>
<!-- 特定ユーザーの書籍情報を全件取得 -->
<select id="findByOne" resultMap="bookMapper">
SELECT
b.book_id,
b.book_name,
b.book_price,
b.book_page,
b.book_current_page,
b.book_img_name,
b.book_img,
b.book_category_id,
c.category_name,
b.book_user_id,
u.user_name,
b.create_at,
b.update_at
FROM
books AS b
JOIN
categories AS c
ON
b.book_category_id = c.category_id
JOIN
users AS u
ON
b.book_user_id = u.user_id
WHERE
b.book_user_id = #{bookUserId}
ORDER BY
b.book_id;
</select>
<!-- 特定ユーザーの中から書籍idが一致する書籍情報の取得 -->
<select id="findById" resultMap="bookMapper">
SELECT
b.book_id,
b.book_name,
b.book_price,
b.book_page,
b.book_current_page,
b.book_img_name,
b.book_img,
b.book_category_id,
c.category_name,
b.book_user_id,
u.user_name,
b.create_at,
b.update_at
FROM
books AS b
JOIN
categories AS c
ON
b.book_category_id = c.category_id
JOIN
users AS u
ON
b.book_user_id = u.user_id
WHERE
b.book_user_id = #{bookUserId}
AND
b.book_id = #{bookId}
ORDER BY
b.book_id;
</select>
<!-- 書籍名が一致する情報の取得 -->
<select id="findByName" resultMap="bookMapper">
SELECT
b.book_id,
b.book_name,
b.book_price,
b.book_page,
b.book_current_page,
b.book_img_name,
b.book_img,
b.book_category_id,
c.category_name,
b.book_user_id,
u.user_name,
b.create_at,
b.update_at
FROM
books AS b
JOIN
categories AS c
ON
b.book_category_id = c.category_id
JOIN
users AS u
ON
b.book_user_id = u.user_id
WHERE
b.book_user_id = #{bookUserId}
AND
b.book_name LIKE '%${bookName}%'
ORDER BY
b.book_id;
</select>
<!-- 書籍情報の更新 -->
<update id="updateBook">
UPDATE
books
<set>
<if test="bookName != null">
book_name = #{bookName},
</if>
<if test="bookCategoryId != null">
book_category_id = #{bookCategoryId},
</if>
<if test="bookPrice != null">
book_price = #{bookPrice},
</if>
<if test="bookImg != null">
book_img = #{bookImg},
</if>
<if test="bookCurrentPage != null">
book_current_page = #{bookCurrentPage}
</if>
</set>
WHERE
book_id = #{bookId}
AND
book_user_id = #{bookUserId};
</update>
</mapper>
book-list.html(書籍一覧を表示)
<!-- 書籍の一覧表示 -->
<div class="container">
<div class="row">
<div th:each="book : ${bookList}" class="col">
<div th:text="${book.bookName}">書籍名</div>
<img th:scr="@{'/Users/manami/workspace/yonda/books/' + ${book.bookImgName}}" class="img-responsive img-rounded item-img-center" width="200">
</div>
</div>
</div>
BookListController.java(ユーザーごとに書籍の一覧表示をするためのコントローラー)
@RequestMapping("/book-list")
public String bookList(MultipartFile multipartFile, Model model) throws IOException {
// ユーザーのIDを取得する
User user = (User)session.getAttribute("user");
Integer userId = user.getUserId();
// ユーザーIDが一致する書籍情報を取得する
List<Book> bookList = bookListService.findByOne(userId);
model.addAttribute("bookList", bookList);
return "book-list";
}
BookListService.java
public List<Book> findByOne(Integer userId) {
return bookMapper.findByOne(userId);
}
3