はじめに
私は今、学校の課題でECサイトをチームで開発しています。その際に、画面から指定された画像をMySQLデータベースのBlob型のカラムに保存するのに手間取ったので、そのやり方を記事として投稿します。
やりたいこと
画面で画像を選択する
↓
画面遷移して選択した画像を表示する
↓
画像をデータベースに格納する
画面から画像を選択する
<form action="displayImage" method="post" enctype=multipart/form-data>
<p>画像</p>
<input type="file" name="image" accept="image/*">
<input type="submit" value="送信">
</form>
上記のようにinputタグのtype
をfile
に指定することでバイナリファイルを選択することができるようになります。
その際にformタグでenctype=multipart/form-data
を指定します。これを指定することで複数の種類のデータをサーバーに送信することができるようになります。
また、inputタグでaccept="image/*"
を選択することで選択できるファイルを画像のみに制限することができます。
選択された画像を取得する
@WebServlet("/displayImage")
@MultipartConfig(location = "/tmp")
public class DisplayImageServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part = request.getPart("image");
InputStream inputStream = part.getInputStream();
byte[] byte = convertInputStreamToByteArray(inputStream);
ImageBean imageBean = new ImageBean;
imageBean.setImage(byte);
HttpSession session = request.getSession();
session.setAttribute("imageBean", imageBean);
request.getRequestDispatcher("WEB-INF/jsp/image_view.jsp").forward(request, response);
}
//InputStreamをByte配列にする
public byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16777215];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
}
}
画面から送信された画像を取得するServletでは、@MultipartConfig
アノテーションをつける必要があります。
multipart/form-dataで送信されたデータは、getParameter()
ではなくgetPart()
で取得します。
その後、取得したデータをInputStream
に変換し、さらにバイト配列に変換します。
そして、変換したバイト配列をBeanに格納し、セッションに保存します。
確認画面で画像を表示する
<p>画像</p>
<img src="getImage" alt="画像">
<form action="uploadImage" method="post">
<input type="submit" value="格納">
</form>
@WebServlet("/getImage")
public class GetImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
InputStream inputStream = new ByteArrayInputStream(((ImageBean) session.getAttribute("imageBean")).getImage());
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
BufferedImage img = ImageIO.read(bufferedInputStream);
response.setContentType("image/png");
OutputStream outputStream = response.getOutputStream();
ImageIO.write(img, "png", outputStream);
outputStream.flush();
}
}
imgタグのsrc
に画像取得用のGetImageServlet
のアノテーションを指定することでServletを介して画像を取得します。
GetImageServlet
では、先ほどセッションに保存したImageBean
から画像のバイト配列を取得し、InputStreamに戻した後に、それを読み込んで画面に返しています。
画像をデータベースに格納する
CREATE TABLE `image` (
`image` mediumblob
)
@WebServlet("/uploadImage")
public class UploadImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
ImageBean imageBean = (ImageBean) session.getAttribute("imageBean");
ImageDao imageDao = new ImageDao();
imageDao.uploadImage(imageBean.getImage());
}
}
public class ImageDao {
public void uploadImage(byte[] byte){
PreparedStatement stmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/image", "root", "root");
stmt = con.prepareStatement("INSERT INTO image (image) VALUES (?)");
stmt.setBinaryStream(1, new ByteArrayInputStream(byte));
stmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
try {
if (con != null) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
画像をデータベースに格納します。バイト配列をInputStream
に変換したものをSQLのプレースホルダーにsetBinaryStream()
を使って格納します。最後にSQLを発行すればデータベースへの画像の格納は完了です。
ここで、もしDisplayImageServlet
で画像データをInputStream
に変換した後、バイト配列に変換していないと、画像をうまくデータベースに格納することができません。
バイト配列に変換しない場合、画像を表示する際にGetImageServlet
でInputStream
をread
するとデータの最後まで読み込むので、ImageDao
でsetBinaryStream()
を使うときにInputStream
の終端からの値を格納するのが原因っぽい?
参考
[フロントエンド] multipart/form-dataを理解してみよう
JSP/サーブレット ファイルアップロードの実装
サーブレット・JSPでDBに登録されている画像を表示