JavaEE servlet & jsp で画像のアップロードファイルの一覧・画像表示が出来ません
問題点
Java初心者です。
先日servlet&jspで画像アップロードファイルの作成が出来ませんでご質問させて頂いた続きでもあり、度々恐縮ですがお願い致します。
jspのフォームからtype属性fileで受け取った画像ファイルを、
アプリケーションを再起動しても保存できる場所へ保存して、表示させたいのですが、
ひとまず、webContent外に upload ディレクトリを作成しupload->保存->すぐに表示させることを目標にコードを書いています。
ご教示頂いたおかげで、ファイルは作成できている(?)ようなのですが、
その確認作業として保存されたファイルの一覧を表示させようとしましたが、上手くいきません。
① 一覧表示に関して、
・単にコードが間違っている。
・例外は発生していないけど、実際には仮想パス上にファイルは作られていない。
(当時は作成されていたとしても、その場が保障されていない事例なのか知りたいです。)
のか、
② 画像表示に関して、
・単にコードが間違っている。
(使用するクラス,手段が間違っている)
・①に関連して、指定したパス上にファイルがない為読み込めていなくて、nullだから表示出来ていない。
のか、
が分からないのでご教示頂けないでしょうか。
一部ソースコードは載せていますが、量が多いので、
部分的な助言や、概念の情報だけでもいただけると幸いです。
開発環境
● Eclipse Version: 2019-03 (4.11.0), Tomcat9 , java11
● MacOS Mojave, Version: 10.14.4)
以下コードになります。
package servlet;
import java.io.*;
(略)
import model.CheckDirectoryContentsLogic;
import model.FileUploadLogic;
import model.GetFileNameLogic;
import model.IsValidFileLogic;
@WebServlet("/FileUpload")
@MultipartConfig(maxFileSize=10485760,location="/tmp/")
public class FileUpload extends HttpServlet {
private static final long serialVersionUID = 1L;
public FileUpload() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String title = request.getParameter("title");
Part part = request.getPart("data");
String nameadd = new StringBuilder(title).append("_").append(System.currentTimeMillis()).toString();
String filename = new GetFileNameLogic().getFileName(part);
String uploadDir = getServletContext().getRealPath("/upload");
Path file = null;
try {
file = Paths.get(uploadDir,nameadd);
} catch (InvalidPathException e1) {
e1.printStackTrace();
throw new InvalidPathException(file.toString(),"パス文字列をPathに変換できません");
}
Path createdDirectry=null;
try {
createdDirectry=Files.createDirectories(file);
} catch (UnsupportedOperationException e1) {
e1.printStackTrace();
throw new UnsupportedOperationException("ディレクトリの作成時に原子的に設定できない属性が配列に含まれる");
} catch (FileAlreadyExistsException e1) {
e1.printStackTrace();
throw new FileAlreadyExistsException ("dirが存在するが、ディレクトリではない(オプションの固有例外)");
} catch (IOException e1) {
e1.printStackTrace();
throw new IOException ("入出力エラー");
}
Path createdfile = Paths.get(createdDirectry.toString(),filename);
try {
createdfile=Files.createFile(createdfile);
} catch (UnsupportedOperationException e) {
e.printStackTrace();
throw new UnsupportedOperationException ("原子的に設定できない属性が配列に含まれます");
} catch (FileAlreadyExistsException e) {
e.printStackTrace();
throw new FileAlreadyExistsException ("同名ファイルがすでに存在します");
}catch (NoSuchFileException e) {
e.printStackTrace();
throw new NoSuchFileException("ファイルが作成できませんでした");
}
if(new IsValidFileLogic().execute(filename)) {
FileUploadLogic flogic =new FileUploadLogic ();
try {
flogic.execute(part,createdfile);
}catch (IllegalArgumentException e) {
e.printStackTrace();
throw new IllegalArgumentException ("inputstream sizeover");
}catch (NoSuchFileException e) {
e.printStackTrace();
throw new NoSuchFileException("ファイルが存在しません2");
}catch (IOException e) {
e.printStackTrace();
throw new IOException("ファイル書き込み入出力エラー(Files.line()?BufferedInputStream.read()?.write()?)");
}
}
CheckDirectoryContentsLogic cdlogic=new CheckDirectoryContentsLogic();
String msg=cdlogic.checkDirectry(createdfile);
request.setAttribute("msg",msg);
request.setAttribute("createdfile", createdfile.toString());
File[] files=cdlogic.filespath(createdDirectry);
List<String> fileName=new ArrayList<>();
for(File f:files) {
fileName.add(f.getAbsolutePath());
}
request.setAttribute("files",filename);
String to="/WEB-INF/jsp/uploadResult.jsp";
RequestDispatcher dsp=request.getRequestDispatcher(to);
dsp.forward(request, response);
}
}
ファイル名を取得するBO
package model;
import javax.servlet.http.Part;
public class GetFileNameLogic{
public String getFileName(Part part) {
String name= null;
for (String dispotion : part.getHeader("Content-Disposition").split(";")) {
if (dispotion.trim().startsWith("filename")) {
name = dispotion.substring(dispotion.indexOf("=") + 1).replace("\"", "").trim();
name=name.substring(name.lastIndexOf("\\")+1);
break;
}
}
return name;
}
}
取り込むデータの拡張子を判断するBO
package model;
public class IsValidFileLogic {
public boolean execute(String name){
if (name != null) {
String[] perms = { "jpg", "jpeg", "png"};
String[] names = name.split("\\.");
for (String perm: perms) {
if (perm.equals(names[names.length - 1])) {
return true;
}
}
}
return false;
}
}
作成したファイルにアップロードするBO
package model;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import javax.servlet.http.Part;
public class FileUploadLogic {
public void execute(Part part,Path path) throws IOException {
try(BufferedInputStream br=new BufferedInputStream(part.getInputStream(), (int)part.getSize());
ByteArrayOutputStream bw=new ByteArrayOutputStream()){
int count=0;
byte[] buff=new byte[(int)part.getSize()];
while((count=br.read(buff)) != -1) {
bw.write(buff, 0, count);
}
}
}
}
引数のパスのファイルの存在を調べるcheckDirectry()と
引数のパスのDirectryにあるファイルのパスを取得するfilespath()
を持つBO
package model;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class CheckDirectoryContentsLogic {
public String checkDirectry(Path path) {
String msg=path.toAbsolutePath().toString();
if(Files.exists(path)) {
msg=msg+"は存在します";
}else {
msg=msg+"は存在しません";
}
return msg;
}
public File[] filespath(Path path) throws IOException{//this method 4 List of File
File[] files=new File(path.toString()).listFiles();
return files;
}
}
フォームのjsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Input</title>
</head>
<body>
<FORM ACTION ="/upload_imge/FileUpload" METHOD="post" ENCTYPE="MULTIPART/FORM-DATA"><%--Content-Typeが2つ--%>
TITLE<INPUT TYPE="text" NAME="title"/><BR>
DATA<INPUT TYPE="file" NAME="data" size="40"/><BR>
<INPUT TYPE="submit">
</FORM>
</body>
</html>
結果を表示するjsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>uploaded files</title>
</head>
<body>
<p>アップロードされたファイル一覧:<br>
<c:forEach var="path" items="files">
<c:out value="${path}"></c:out><br>
</c:forEach></p>
今回のアップロードファイル、
<c:out value="${msg}"/><br>
<img src="${createdfile}"><br>
<img src="https://picsum.photos/200/300"><br>
<img src="http://localhost:8080/Applications/Eclipse_2019- 03.app/Contents/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/upload_imge/upload/Penguin_1561013398735/1-200x300.jpg">
</body>
</html>
追記・修正分
jsp修正分(部分)
<p>アップロードされたファイル一覧:<br>
<c:forEach var="path" items="files">
<c:out value="${path}"></c:out><br>
</c:forEach></p>
今回のアップロードファイル、
<c:out value="${msg}"/><br>
<c:out value="${relativePath}"></c:out><br>
<img src="${relativePath}"><br>
<img src="https://picsum.photos/200/300"><br>
<img src="http://localhost:8080/upload_imge/upload/Penguin_1561013398735/1- 200x300.jpg">
<img src="http://localhost:8080/upload_imge/upload/${relativePath}">
※<img src="http://localhost:8080/upload/Penguin_1561013398735/1- 200x300.jpg">
は、今回より前に作成して表示されたパスにまだファイルが存在していて表示できるかの確認のためのURLです。
試した事・その他原因として考えた事 等
①一覧表示に関して
servletで取得した List型のパス名一覧を、requestスコープに保存し、forward先jspで
<c:forEach var="path" items="files">
として表示させたいのですが、一度だけ
files
と表示されてしまいます。
この前後コード修正をして実行した際に、「レスポンスをコミットした後でフォーワード出来ません」のような内容の例外が出ており、
Eclipse によって自動生成された、doPost
メソッド最後のdoGet(request, response);
を消したら上手くいった経緯もあり、スコープに保存されていないのか?とも考えましたが、それ以上突き詰める事が出来ませんでした。
②画像表示に関して
・jspタグでのファイル指定先先頭にhttp://localhost:8080/
を追加
(画像自体が表示できるのかの確認のため、クラウドソースの画像URLを入れています。表示されているのはその画像です。)
ネットで、「一つのservlet response で文字列と画像を同時に表示する事は出来ない」といった内容のFAQを発見したのですが、
そもそも、ここで表示させること自体間違いなのでしょうか。
・画像イメージを表示させるには、バイナリデータのままでは無理なのでは?と思いImageIO
を使用してみましたが、
1度目、
java.lang.IllegalArgumentException: image == null!
の例外が出たので、「ImageIO.read()で読み込んだら、ImageIO.write()でストリームへ書込まなければいけない」のか?と思い
変更すると、
「ページのロードでエラーが発生しました」のウインドウが表示されてしまいました。
又、上記は別の疑問点にもなるのですが、ImageIO.read()で読み込んだら、ImageIO.write()でストリームへ書込まなければいけないのでしょうか。
例えば、「別の手段でデータのbyte配列を取得して、ImageIO.write()
でも可能なのか」、等の互換性の問題もいまいち良く分かりません。
仮に出来たとしても何らかの理由で特定の手段にすることが望ましい場合は、その点もお教えいただけると幸いです。
初心者すぎて、疑問点の焦点が定まっておらず申し訳ありません。
間違って解釈していることがすっきりしないので、皆様のお知恵を拝借させていただきたく思います。
どうぞよろしくお願い致します。