サーブレットを使ったファイルのアップロードとダウンロードの例
Javaウェブアプリケーションでの「Servletファイルのアップロード」と「ファイルのダウンロード」は一般的なタスクです。最近Javaサーブレットについて多くの記事を書いてきたので、サンプルとしてサーブレットのファイルアップロードの例をサーバーに提供し、それをサーバーからクライアントにダウンロードします。
サーブレットでファイルをアップロードする
当社のユースケースは、クライアントがローカルファイルを選択してサーバーにアップロードできるシンプルなHTMLページを提供することです。ファイルをアップロードするためのリクエストを提出すると、当社のサーブレットプログラムがファイルをサーバーのディレクトリにアップロードし、ユーザーがファイルをダウンロードできるURLを提供します。セキュリティ上の理由から、ユーザーにはファイルの直接のダウンロードURLは提供されず、代わりにダウンロードリンクが提供され、当社のサーブレットがリクエストを処理し、ファイルをユーザーに送信します。Eclipseで動的なWebプロジェクトを作成し、プロジェクトの構造は以下の画像のようになります。Webアプリケーションのすべてのコンポーネントを見て、実装を理解しましょう。
サーバーへのファイルのアップロードのためのJava HTML ページ
フォームを送信し、POSTリクエストをサーブレットに送信することで、ファイルをサーバーにアップロードすることができます。ファイルをアップロードするためにGETメソッドは使用できませんので注意が必要です。また、フォームのenctypeはmultipart/form-dataである必要があります。ユーザーのファイルシステムからファイルを選択するには、ファイルの入力要素を使用する必要があります。そのため、ファイルをアップロードするためのシンプルな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.scdev.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) {
//do cleanup if needed
}
}
ファイルのアップロードとダウンロードのサーブレット
アップデート: Servletの仕様3では、APIにサーバー上にファイルをアップロードするサポートが追加されましたので、サードパーティーのAPIを使用する必要はありません。Servlet 3アップロードファイルをご確認ください。ファイルのアップロードには、Apache Commons FileUploadユーティリティを使用します。プロジェクトではバージョン1.3を使用しています。FileUploadにはApache Commons IO jarが必要ですので、両方をプロジェクトのlibディレクトリに配置する必要があります。画像の上に示されているように、プロジェクトの構造で確認できます。HttpServletRequestオブジェクトをパースしてFileItemのリストを返すメソッドを提供するDiskFileItemFactoryファクトリを使用します。FileItemには、アップロードするファイルのファイル名、フォーム内のフィールド名、サイズ、コンテンツタイプの詳細を取得するための便利なメソッドがあります。ディレクトリにファイルを書き込むためには、Fileオブジェクトを作成し、それをFileItemのwrite()メソッドの引数として渡すだけです。サーブレットの目的はファイルをアップロードすることですので、init()メソッドをオーバーライドして、サーブレットのDiskFileItemFactoryオブジェクトのインスタンスを初期化します。このオブジェクトをdoPost()メソッドの実装で使用して、ファイルをサーバーディレクトリにアップロードします。ファイルのアップロードが成功したら、ファイルのダウンロードURLを含んだレスポンスをクライアントに送信します。HTMLのリンクはGETメソッドを使用するため、URLにファイル名のパラメータを追加し、同じサーブレットのdoGet()メソッドを使用してファイルのダウンロードプロセスを実装できます。ファイルのダウンロードサーブレットを実装するためには、まずファイルのInputStreamを開き、ServletContext.getMimeType()メソッドを使用してファイルのMIMEタイプを取得し、レスポンスのコンテンツタイプとして設定します。また、ファイルの長さとしてレスポンスのコンテンツ長さを設定する必要があります。ファイルを送信することをクライアントが理解するためには、”Content-Disposition”ヘッダーの値を”attachment; filename=“fileName””に設定する必要があります。レスポンスの設定が完了したら、InputStreamからファイルのコンテンツを読み取り、ServletOutputStreamに書き込み、出力をクライアントにフラッシュします。最終的なUploadDownloadFileServletサーブレットの実装は以下のようになります。
package com.scdev.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>");
}
}
プロジェクトのサンプル実行は以下の画像で示されています。 (Purojekuto no sanpuru jikkō wa izanai no gazō de shimesareteimasu.)
サーブレットファイルのアップロードダウンロードプロジェクトをダウンロードする。
下記の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
サーブレットファイルのアップロードダウンロードのサンプルプロジェクトをダウンロードしてください。
シリーズの次の記事で、サーブレットの例外処理について確認してください。