1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JSPとServletでファイルアップロード

Last updated at Posted at 2022-10-24

JSP と Servlet 使ってファイルアップロードをやってみる

アップロードでやるざっくり内容

  • フォルダ
    • アップロード用のフォルダ作成 /java-webapp-clump/src/main/webapp/upload
    • tomcat で読み込まれるように blankファイルを配置
  • JSP
    • 入力送信用のformタグに enctype="multipart/form-data" を追加
    • 入力送信用のformタグの中にある input タグに type="file" を追加
    • アップロード表示用のタグを配置 (ここでは 画像 img, 動画(・音声) video のサンプルでやってみます )
  • Servlet
    • クラスのアノテーションに @MultipartConfig を追加
    • request.getPart("JSPで設定したname") で入力したファイルを取得
    • getServletContext() でサーバに展開してるフォルダパスを取得し /upload/アップロードファイル名 で保存
    • JSP に保存したファイルパス ( /upload/アップロードファイル名 ) を返す

※アップロード直後に表示すると保存・転送が間に合わずにアップロードファイルが開けない...
※リロードすればそのうち取得できます
※サンプルではJavaScriptの処理いれて簡易なローディングっぽいの差し込んでみてます...

JSP, Servlet 使ってファイルアップロード(ファイル保存して表示)

ここで使ってるサンプルのおおもとはこちら JSP と Servlet ざっくりまとめ を参考にお願いします

1. アップロードファイルを保存するフォルダを作成

  • /java-webapp-clump/src/main/webapp/ の直下に フォルダ upload を作成
  • フォルダ upload の直下に blank ファイルを作成
    image.png

2. JSP : 入力送信用のタグ(form タグ) にファイルアップロード設定, アップロード表示用のタグを配置

  • /java-webapp-clump/src/main/webapp/WEB-INF/view/uploadFile.jsp を作成
  • form タグの action="upload_file", method="post", enctype="multipart/form-data" を設定
  • form タグの中の input タグに type="file" を追加, 必須チェックとして required を設定
uploadFile.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ page isELIgnored="false" %>

<!doctype html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>JavaDevelop</title>
    <link rel="shortcut icon" href="<c:url value="/assets/favicon.ico" />">
    <link rel="stylesheet" href="<c:url value="/assets/bootstrap.min.css" />">
    <link rel="stylesheet" href="<c:url value="/assets/bootstrap-icons.css" />">
    <link rel="stylesheet" href="<c:url value="/assets/styles.css" />">
</head>

<body>

    <%@ include file="./_headerNavbar.jsp" %>

    <main>
        <div class="container workspace">
            <form action="upload_file" method="post" enctype="multipart/form-data">
                <label>ファイル 画像:</label>
                <input type="file" name="img" />
                <br/>
                <label>ファイル 音声:</label>
                <input type="file" name="audio" />
                <br/>
                <label>ファイル 動画:</label>
                <input type="file" name="video" />
                <br/>
                <input type="submit" />
            </form>
            <hr/>
            <div class="upload">
                <p>アップロードファイル:画像</p>
                <c:if test="${!empty resultDto.imgPath}">
                    <div class="spinner-border d-none uploadFileLoading"></div>
                    <img class="uploadFile" src="<c:url value="${resultDto.imgPath}" />" />
                </c:if>
            </div>
            <hr/>
            <div class="upload">
                <p>アップロードファイル:音声</p>
                <c:if test="${!empty resultDto.audioPath}">
                    <div class="spinner-border d-none uploadFileLoading"></div>
                    <audio class="uploadFile" src="<c:url value="${resultDto.audioPath}" />" controls></audio>
                </c:if>
            </div>
            <hr/>
            <div class="upload">
                <p>アップロードファイル:動画</p>
                <c:if test="${!empty resultDto.videoPath}">
                    <div class="spinner-border d-none uploadFileLoading"></div>
                    <video class="uploadFile" src="<c:url value="${resultDto.videoPath}" />" controls></video>
                </c:if>
            </div>
            <hr/>
        </div>
    </main>

    <footer class="footer mt-auto fixed-bottom py-3 bg-secondary"></footer>

    <script src="<c:url value="/assets/bootstrap.bundle.min.js" />"></script>
    <script src="<c:url value="/assets/script.js" />"></script>
</body>

</html>

3. Servlet : ファイル保存してJSPに保存したファイルを返す

  • /java-webapp-clump/src/main/java/presentation/servlet/UploadFileServlet.java を作成
  • クラスのアノテーションに @WebServlet("/uplaod_file"), @MultipartConfig を設定
  • Part part = request.getPart("JSPで設定した name "); で入力したファイルを取得
  • String filePath = "/upload/" + part.getSubmittedFileName(); でJSPで使うファイルパスを作成
  • part.write(getServletContext().getRealPath(filePath)); でアップロードしたファイルをサーバに保存
  • request.setAttribute("resultDto", new Dto(filePath)); でファイルパスを DTO に設定して JSP に返す
    ※ DTO(UploadFile.java) を作成, インスタンス変数 imgPath, audioPath, videoPath を追加してコンストラクタ、getter(, setter) を追加しておく
UplaodFileServlet.java
package presentation.servlet;

import java.io.IOException;
import java.util.function.Consumer;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import dto.UploadFile;

/**
 * {@link UploadFileServlet}
 */
@WebServlet("/upload_file")
@MultipartConfig
public class UploadFileServlet extends HttpServlet {

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String view = "/WEB-INF/view/uploadFile.jsp";
		RequestDispatcher dispatcher = request.getRequestDispatcher(view);
		dispatcher.forward(request, response);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");

		UploadFile dto = new UploadFile();
		fileUpload(request.getPart("img"), (s) -> dto.setImgPath(s));
		fileUpload(request.getPart("audio"), (s) -> dto.setAudioPath(s));
		fileUpload(request.getPart("video"), (s) -> dto.setVideoPath(s));
		request.setAttribute("resultDto", dto);

		doGet(request, response);
	}

	/**
	 * ファイルアップロードしてファイルパスを設定します
	 *
	 * @param part パート
	 * @param function ファイルパス設定処理
	 * @throws IOException ファイル書込時の例外
	 */
	private void fileUpload(Part part, Consumer<String> function) throws IOException {
		if ("".equals(part.getSubmittedFileName())) {
			return;
		}
		String filePath = "/upload/" + part.getSubmittedFileName();
		part.write(getServletContext().getRealPath(filePath));
		function.accept(filePath);
	}
}
UploadFile.java
package dto;

/**
 * {@link UploadFile}
 */
public class UploadFile {

	/** 画像パス */
	private String imgPath;

	/** 音声パス */
	private String audioPath;

	/** 動画パス */
	private String videoPath;

	/**
	 * コンストラクタ
	 */
	public UploadFile() {
	}

	/**
	 * コンストラクタ
	 * @param imgPath 画像パス
	 * @param audioPath 音声パス
	 * @param videoPath 動画パス
	 */
	public UploadFile(String imgPath, String audioPath, String videoPath) {
		this.imgPath = imgPath;
		this.audioPath = audioPath;
		this.videoPath = videoPath;
	}

	/**
	 * @return imgPath
	 */
	public String getImgPath() {
		return imgPath;
	}

	/**
	 * @param imgPath セットする imgPath
	 */
	public void setImgPath(String imgPath) {
		this.imgPath = imgPath;
	}

	/**
	 * @return audioPath
	 */
	public String getAudioPath() {
		return audioPath;
	}

	/**
	 * @param audioPath セットする audioPath
	 */
	public void setAudioPath(String audioPath) {
		this.audioPath = audioPath;
	}

	/**
	 * @return videoPath
	 */
	public String getVideoPath() {
		return videoPath;
	}

	/**
	 * @param videoPath セットする videoPath
	 */
	public void setVideoPath(String videoPath) {
		this.videoPath = videoPath;
	}

}

4. ここまでやったら動作確認

  • http://localhost:8080/java-wabapp-clump/upload_file をブラウザで表示
  • ファイルを選択して送信を実行
  • ブラウザ表示でエラーがないのにアップロードファイル表示されない時は更新すれば表示されるはず

XX. アップロードファイルが表示されるように自動更新を JavaScript でやってみる

uploadFile.jsp にローディング用のタグはすでに設定済みなので JavaScript のみ対応を追加

XX-1. JavaScript にアップロードファイル更新の処理を追加

  • /java-webapp-clump/src/main/webapp/assets/script.js を修正・追記
  • html 読込時の処理 に function(メソッド)呼び出しを追加 applyUploadFileWait();
  • function(メソッド) ファイル更新待ち設定 applyUploadFileWait() 作成
  • function(メソッド) ファイル更新 updateSrc(dom) 作成
  • function(メソッド) ファイル表示 loadSrc(dom, domLoading) 作成
script.js



/**
 * html 読込時の処理
 */
window.addEventListener('load', (e) => {
  pageLinks.forEach((links, i) => {
    applyNavbar(links);
    applyCarousel(links, i);
  });
  // 追加するのは下の行のfunction(メソッド)呼び出し
  applyUploadFileWait();
});


// function(メソッド)を追加
/**
 * アップロードファイル更新待ち設定
 */
function applyUploadFileWait() {
  document.querySelectorAll('.upload').forEach(uploadDom => {
    let dom = uploadDom.querySelector('.uploadFile');
    if (!dom || !['IMG', 'VIDEO', 'AUDIO'].includes(dom.tagName)) return;

    let domLoding = uploadDom.querySelector('.uploadFileLoading');
    dom.onerror = () => updateSrc(dom);
    if (dom.tagName == 'IMG') {
      dom.onload = () => loadSrc(dom, domLoding);
    } else {
      dom.onloadedmetadata = () => loadSrc(dom, domLoding);
    }

    dom.classList.add('d-none');
    domLoding.classList.remove('d-none');
    updateSrc(dom);
  });
}

/**
 * ファイル更新
 * 
 * @param dom
 *            メディアDOM(img, video など)
 */
function updateSrc(dom) {
  let src = dom.src;
  dom.src = '';
  dom.src = src;
}

/**
 * ファイル表示
 * 
 * @param dom
 *            メディアDOM(img, video など)
 * @param domLoding
 *            ローディングDOM
 */
function loadSrc(dom, domLoding) {
  domLoding.classList.add('d-none');
  dom.classList.remove("d-none");
}

XX-2. ここまでやったら動作確認

ここから先はご自由に!

画像、動画、音声を分けて登録できるようにするとかでそれっぽくなりそう
いれたやつの一覧表示とか...

JSP, Servlet 使ってファイルアップロード(サンプル WorkServlet.java, work.jsp に追加)

ここで使ってるサンプルのおおもとはこちら JSP と Servlet ざっくりまとめ を参考にお願いします
WorkServlet.java, work.jsp に追加する形で進めてます

1. アップロードファイルを保存するフォルダを作成

  • /java-webapp-clump/src/main/webapp/ の直下に フォルダ upload を作成
  • フォルダ upload の直下に blank ファイルを作成
    image.png

2. JSP : 入力送信用のタグ(form タグ) にファイルアップロードの設定を追加

  • /java-webapp-clump/src/main/webapp/WEB-INF/view/work.jsp を修正(mainタブのdivタブ(container workspace)の中)
  • form タグに enctype="multipart/form-data" を追加
  • form タグの中の input タグに type="file" を追加, 必須チェックとして required を設定
work.jsp
            <!-- 追加するのは下の行の enctype="multipart/form-data" の部分 -->
            <form action="web_app_work" method="post" enctype="multipart/form-data">
                <label>入力:</label>
                <input type="text" name="input1" />
                <!-- 追加するのはここから4行 input タブの name はなんでもOK, ここでは次の Servlet と合わせて file1 にしてる -->
                <br/>
                <label>ファイル:</label>
                <input type="file" name="file1" required />
                <br/>
                <!-- 追加するのはここまで -->
                <input type="submit" />
            </form>

3. Servlet : ファイル保存してJSPに保存したファイルを返す

  • /java-webapp-clump/src/main/java/presentation/servlet/WorkServlet.java を修正(doPostメソッドの中)
  • クラスのアノテーションに @MultipartConfig を追加
  • Part part = request.getPart("file"); で入力したファイルを取得
  • String filePath = "/upload/" + part.getSubmittedFileName(); でJSPで使うファイルパスを作成
  • part.write(getServletContext().getRealPath(filePath)); でアップロードしたファイルをサーバに保存
  • request.setAttribute("resultDto", new Work(input1, filePath)); でファイルパスを DTO に設定して JSP に返す
    ※ DTO にインスタンス変数 filePath を追加してコンストラクタ、getter(, setter) を追加しておく
WorkServlet.java

@WebServlet("/web_app_work")
@MultipartConfig // この行を追加
public class WorkServlet extends HttpServlet {





	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");

		String input1 = request.getParameter("input1");
		request.setAttribute("result1", "入力したのは " + input1);

		// 追加・修正するのはここから6行, コメントアウトしてる sysout は実際の保存するファイルパス確認用
		Part part = request.getPart("file1");
		String filePath = "/upload/" + part.getSubmittedFileName();
		part.write(getServletContext().getRealPath(filePath));
		// System.out.println(getServletContext().getRealPath(filePath));

		request.setAttribute("resultDto", new Work(input1, filePath));
		// 追加・修正するのはここまで

		doGet(request, response);
	}





}

念のため Work.java の修正・追加箇所をピックアップ

Work.java

	/** ファイルパス */
	private String filePath;

	/**
	 * コンストラクタ
	 * @param input 入力値
	 * @param input ファイルパス
	 */
	public Work(String input, String filePath) {
		this.input = input;
		this.filePath = filePath;
	}

	/**
	 * filePath を取得します
	 * @return filePath
	 */
	public String getFilePath() {
		return filePath;
	}

	/**
	 * filePath を設定します
	 * @param filePath ファイルパス
	 */
	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}

4. JSP : 表示用のタグを配置(画像 img, 動画(・音声) video)

  • /java-webapp-clump/src/main/webapp/WEB-INF/view/work.jsp を修正(mainタブのdivタブ(container workspace)の中)
  • taglibの c:if を使って resultDto.filePath があるときのみ表示用タグを配置するようにする
  • 画像アップロードのときは imgタグ、動画(・音声)アップロードのときは videoタグを使う
  • img or video タグの src に <c:url value="${resultDto.filePath}" /> を設定してアップロード保存したファイルを指定
work.jsp
    <main>
        <div class="container workspace">
            ・
            ・
            ・
            <!-- 追加するのは divタグ(container workspace) の最後のところ -->
            <hr/>
            <p>アップロードファイルを表示してみる</p>
            <c:if test="${!empty resultDto.filePath}">
                <!-- 画像アップロードのとき ※どっちかのみ残す-->
                <img class="uploadFile" src="<c:url value="${resultDto.filePath}" />" />
                <!-- 動画(・音声)アップロードのとき ※どっちかのみ残す -->
                <video class="uploadFile" src="<c:url value="${resultDto.filePath}" />" controls></video>
            </c:if>

        </div>
    </main>

5. ここまでやったら動作確認

image.png

XX. アップロードファイルが表示されるように自動更新を JavaScript でやってみる

XX-1. JSP にローディングの枠を追加

  • /java-webapp-clump/src/main/webapp/WEB-INF/view/work.jsp を修正(mainタブのdivタブ(container workspace)の中)
  • ローディング表示のdivタグ <div class="spinner-border d-none uploadFileLoading"></div> を配置
  • メディアタグ、ローディングタグをdivタグ <div class="upload">・・・</div> で括る
work.jsp
    <main>
        <div class="container workspace">
            ・
            ・
            ・
            <hr/>
            <div class="upload">
                <p>アップロードファイルを表示してみる</p>
                <c:if test="${!empty resultDto.filePath}">
                    <!-- 追加するのは以下の行のみ class="spinner-border" がローディングの画像になる -->
                    <div class="spinner-border d-none uploadFileLoading"></div>
                    <!-- 画像アップロードのとき ※どっちかのみ残す -->
                    <img class="uploadFile" src="<c:url value="${resultDto.filePath}" />" />
                    <!-- 動画(・音声)アップロードのとき ※どっちかのみ残す -->
                    <video class="uploadFile" src="<c:url value="${resultDto.filePath}" />" controls></video>
                </c:if>
            </div>
        </div>
    </main>

XX-2. JavaScript にアップロードファイル更新の処理を追加

  • /java-webapp-clump/src/main/webapp/assets/script.js を修正・追記
  • html 読込時の処理 に function(メソッド)呼び出しを追加 applyUploadFileWait();
  • function(メソッド) ファイル更新待ち設定 applyUploadFileWait() 作成
  • function(メソッド) ファイル更新 updateSrc(dom) 作成
  • function(メソッド) ファイル表示 loadSrc(dom, domLoading) 作成
script.js



/**
 * html 読込時の処理
 */
window.addEventListener('load', (e) => {
  pageLinks.forEach((links, i) => {
    applyNavbar(links);
    applyCarousel(links, i);
  });
  // 追加するのは下の行のfunction(メソッド)呼び出し
  applyUploadFileWait();
});


// function(メソッド)を追加
/**
 * アップロードファイル更新待ち設定
 */
function applyUploadFileWait() {
  document.querySelectorAll('.upload').forEach(uploadDom => {
    let dom = uploadDom.querySelector('.uploadFile');
    if (!dom || !['IMG', 'VIDEO', 'AUDIO'].includes(dom.tagName)) return;

    let domLoding = uploadDom.querySelector('.uploadFileLoading');
    dom.onerror = () => updateSrc(dom);
    if (dom.tagName == 'IMG') {
      dom.onload = () => loadSrc(dom, domLoding);
    } else {
      dom.onloadedmetadata = () => loadSrc(dom, domLoding);
    }

    dom.classList.add('d-none');
    domLoding.classList.remove('d-none');
    updateSrc(dom);
  });
}

/**
 * ファイル更新
 * 
 * @param dom
 *            メディアDOM(img, video など)
 */
function updateSrc(dom) {
  let src = dom.src;
  dom.src = '';
  dom.src = src;
}

/**
 * ファイル表示
 * 
 * @param dom
 *            メディアDOM(img, video など)
 * @param domLoding
 *            ローディングDOM
 */
function loadSrc(dom, domLoding) {
  domLoding.classList.add('d-none');
  dom.classList.remove("d-none");
}

XX-3. ここまでやったら動作確認

image.png

ここから先はご自由に!

画像、動画、音声を分けて登録できるようにするとかでそれっぽくなりそう
いれたやつの一覧表示とか...

JSP, Servlet 使ってファイルアップロード(ファイル保存せず表示)

ファイルを保存せずに直接、ファイルを img タグに表示する方法

1. JSP 作成

  • ファイルアップロード form action="url_path1" method="post" enctype="multipart/form-data"
  • ファイル表示 <img src="url_path2" />
アップロード・ファイル表示
            <form action="url_upload" method="post" enctype="multipart/form-data">
                <label>ファイル:</label>
                <input type="file" name="file1" required />
                <input type="submit" />
            </form>
            <hr/>
            <img src="url_image" />
            <hr/>

2. Servlet 作成

  • ファイルアップロード:画像を byte[] にする
  • ファイル表示:response.setContentType("image/png"); の設定, BufferedImage を書き込んで返す
アップロード
@WebServlet("/url_upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		Part part = request.getPart("file1");
		request.getSession().setAttribute("imgBytes", readAllBytes(part.getInputStream()));
		doGet(request, response);
	}

	public byte[] readAllBytes(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();
	}
}
ファイル表示
@WebServlet("/url_image")
@MultipartConfig
public class ImageServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		InputStream inputStream = new ByteArrayInputStream((byte[]) request.getSession().getAttribute("imgBytes"));
		BufferedImage img = ImageIO.read(new BufferedInputStream(inputStream));

		response.setContentType("image/png");
		OutputStream outputStream = response.getOutputStream();
		ImageIO.write(img, "png", outputStream);
		outputStream.flush();
	}
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?