Servletファイルのアップロードおよびダウンロードの例

サーブレットのファイルアップロードとダウンロードは、Java Webアプリケーションでよくあるタスクです。最近Java Servletについてたくさん書いてきたので、サーブレットファイルのアップロードとサーバーへのダウンロードのサンプル例を提供しようと思いました。

サーブレットファイルのアップロード

私たちのユースケースは、クライアントがサーバーにアップロードするローカルファイルを選択できる単純なHTMLページを提供することです。ファイルをアップロードするリクエストを送信すると、サーブレットプログラムはファイルをサーバーのディレクトリにアップロードし、ユーザーがファイルをダウンロードできるURLを提供します。セキュリティのため、ユーザーにはファイルをダウンロードするための直接のURLは提供されず、代わりにファイルをダウンロードするためのリンクが与えられ、私たちのサーブレットがリクエストを処理し、ファイルをユーザーに送信します。Eclipseで動的Webプロジェクトを作成し、プロジェクトの構造は以下の画像のようになります。では、Webアプリケーションのすべてのコンポーネントを見て、実装を理解してみましょう。

サーバーにファイルをアップロードするためのJava用のHTMLページ

ファイルをアップロードするには、サーブレットにPOSTリクエストを送信してフォームを送信します。ファイルをアップロードするにはGETメソッドを使用できません。もう1つのポイントは、フォームのenctypemultipart/form-dataである必要があることです。ユーザーファイルシステムからファイルを選択するには、input要素をtypefileで使用する必要があります。したがって、ファイルをアップロードするためのシンプルなHTMLページindex.htmlを作成できます:

<html>
<head></head>
<body>
<form action="UploadDownloadFileServlet" method="post" enctype="multipart/form-data">
Select File to Upload:<input type="file" name="fileName">
<br>
<input type="submit" value="Upload">
</form>
</body>
</html>

ファイルアップロード用のサーバーファイルの場所

ファイルをサーバーに保存する必要があります。プログラム内でこのディレクトリをハードコードすることもできますが、柔軟性を高めるために、展開記述子のコンテキストパラメータで設定可能にします。また、アップロードファイルのHTMLページをウェルカムファイルリストに追加します。次のように、web.xmlファイルが見えるでしょう:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <display-name>ServletFileUploadDownloadExample</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <context-param>
    <param-name>tempfile.dir</param-name>
    <param-value>tmpfiles</param-value>
  </context-param>
</web-app>

ファイルアップロード場所のServletContextListener

ファイルの位置のためのコンテキストパラメータを読み取り、それを使用してFileオブジェクトを作成する必要があるため、コンテキストが初期化されるときにそれを行うためのServletContextListenerを作成できます。他のサーブレットで使用するために、絶対ディレクトリの場所とFileオブジェクトをコンテキスト属性として設定することができます。ServletContextListenerの実装コードは以下のようになります。

package com.journaldev.servlet;

import java.io.File;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class FileLocationContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent servletContextEvent) {
    	String rootPath = System.getProperty("catalina.home");
    	ServletContext ctx = servletContextEvent.getServletContext();
    	String relativePath = ctx.getInitParameter("tempfile.dir");
    	File file = new File(rootPath + File.separator + relativePath);
    	if(!file.exists()) file.mkdirs();
    	System.out.println("File Directory created to be used for storing files");
    	ctx.setAttribute("FILES_DIR_FILE", file);
    	ctx.setAttribute("FILES_DIR", rootPath + File.separator + relativePath);
    }

	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		// 必要に応じてクリーンアップを行う
	}
	
}

ファイルアップロードダウンロードサーブレット

更新:Servlet Specs 3 には、API でサーバーにファイルをアップロードする機能が追加されたため、サードパーティのAPIは必要ありません。 Servlet 3 ファイルのアップロード をご確認ください。ファイルのアップロードには、Apache Commons FileUpload ユーティリティを使用します。プロジェクトではバージョン1.3を使用しています。FileUpload には Apache Commons IO jar が必要なので、プロジェクトの lib ディレクトリに両方を配置する必要があります。プロジェクト構造の上記のイメージで確認できます。 DiskFileItemFactory ファクトリを使用して、HttpServletRequest オブジェクトを解析し、FileItem のリストを返すメソッドが提供されています。FileItem には、アップロードするファイルのファイル名、フォーム内のフィールド名、サイズ、コンテンツタイプの詳細を取得するための便利なメソッドがあります。ディレクトリにファイルを書き込むには、File オブジェクトを作成し、それを FileItem write() メソッドの引数として渡すだけです。サーブレットの目的はファイルをアップロードすることなので、サーブレットの DiskFileItemFactory オブジェクトのインスタンスを初期化するために init() メソッドをオーバーライドします。これを doPost() メソッドの実装で使用して、ファイルをサーバーディレクトリにアップロードします。ファイルが正常にアップロードされたら、ファイルをダウンロードするための URL をクライアントに送信します。HTML リンクは GET メソッドを使用するため、URL にファイル名のパラメータを追加し、同じサーブレットの doGet() メソッドを使用してファイルのダウンロードプロセスを実装できます。ファイルのダウンロードサーブレットを実装するには、まずファイルの InputStream を開き、ServletContext.getMimeType() メソッドを使用してファイルの MIME タイプを取得し、それを応答のコンテンツタイプとして設定します。また、応答のコンテンツ長をファイルの長さとして設定する必要があります。ファイルを応答で送信していることをクライアントが理解するために、「Content-Disposition」ヘッダーを設定する必要があります。値は “attachment; filename=“fileName” です。応答の構成を設定したら、InputStream からファイル内容を読み取り、それを ServletOutputStream に書き込んで出力をクライアントにフラッシュします。UploadDownloadFileServlet サーブレットの最終的な実装は以下のようになります。

package com.journaldev.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/UploadDownloadFileServlet")
public class UploadDownloadFileServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    private ServletFileUpload uploader = null;
	@Override
	public void init() throws ServletException{
		DiskFileItemFactory fileFactory = new DiskFileItemFactory();
		File filesDir = (File) getServletContext().getAttribute("FILES_DIR_FILE");
		fileFactory.setRepository(filesDir);
		this.uploader = new ServletFileUpload(fileFactory);
	}
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String fileName = request.getParameter("fileName");
		if(fileName == null || fileName.equals("")){
			throw new ServletException("File Name can't be null or empty");
		}
		File file = new File(request.getServletContext().getAttribute("FILES_DIR")+File.separator+fileName);
		if(!file.exists()){
			throw new ServletException("File doesn't exists on server.");
		}
		System.out.println("File location on server::"+file.getAbsolutePath());
		ServletContext ctx = getServletContext();
		InputStream fis = new FileInputStream(file);
		String mimeType = ctx.getMimeType(file.getAbsolutePath());
		response.setContentType(mimeType != null? mimeType:"application/octet-stream");
		response.setContentLength((int) file.length());
		response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
		
		ServletOutputStream os = response.getOutputStream();
		byte[] bufferData = new byte[1024];
		int read=0;
		while((read = fis.read(bufferData))!= -1){
			os.write(bufferData, 0, read);
		}
		os.flush();
		os.close();
		fis.close();
		System.out.println("File downloaded at client successfully");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		if(!ServletFileUpload.isMultipartContent(request)){
			throw new ServletException("Content type is not multipart/form-data");
		}
		
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.write("<html><head></head><body>");
		try {
			List<FileItem> fileItemsList = uploader.parseRequest(request);
			Iterator<FileItem> fileItemsIterator = fileItemsList.iterator();
			while(fileItemsIterator.hasNext()){
				FileItem fileItem = fileItemsIterator.next();
				System.out.println("FieldName="+fileItem.getFieldName());
				System.out.println("FileName="+fileItem.getName());
				System.out.println("ContentType="+fileItem.getContentType());
				System.out.println("Size in bytes="+fileItem.getSize());
				
				File file = new File(request.getServletContext().getAttribute("FILES_DIR")+File.separator+fileItem.getName());
				System.out.println("Absolute Path at server="+file.getAbsolutePath());
				fileItem.write(file);
				out.write("File "+fileItem.getName()+ " uploaded successfully.");
				out.write("<br>");
				out.write("<a href=\"UploadDownloadFileServlet?fileName="+fileItem.getName()+"\">Download "+fileItem.getName()+"</a>");
			}
		} catch (FileUploadException e) {
			out.write("Exception in uploading file.");
		} catch (Exception e) {
			out.write("Exception in uploading file.");
		}
		out.write("</body></html>");
	}

}

以下の画像にプロジェクトのサンプル実行が示されています。

Servletファイルのダウンロードプロジェクトをアップロードする

以下のURLからApache Commons IO jarとApache Commons FileUpload jarをダウンロードできます。 https://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi https://commons.apache.org/proper/commons-io/download_io.cgi

Servletファイルのダウンロードの例プロジェクトをダウンロード

次の記事をチェックしてください:サーブレット例外処理に関するシリーズ。

Source:
https://www.digitalocean.com/community/tutorials/servlet-upload-file-download-example