仕事でJava、Servletを使用してファイルダウンロード機能の実装をしたのですが、色々と苦戦をしたので備忘録で残しておきます。
何で残すのか?
同じ事を実現しようと考えている方の参考になれば。
それとこの手の情報が自分が探した中では少なかったので備忘録で残します。
知っていて当たり前だろ!
エンジニアなら知っていて当たり前だろう!と思われるかもしれないが、知らなければ知りませんし知らなければ知るだけです。当たり前ではないと思います。
何に苦戦をしたのか?
半角文字なら何も問題なく実装出来たのですが、2バイト文字は文字化けを起こし、どう解決していいものかあれこれ考えました。
折角それだけ苦戦したのだからそれを残しておこうと思いエントリーをおこしました。
以下がコードになります。
web.xmlとか設定周りは割愛しています。Servlet単体だけ記載してあります。
所々system.out.printlnで出力していますが、それはデバッグ用に出力しているだけなので不要です。
ダウンロード失敗時の処理が今イチですね (; ^ω^)
もっとこうした方がいいのでは?とかそもそもなってない!等のご意見があればコメントからお願いしますm(_ _)m
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeUtility;
public class Download extends HttpServlet {
	/**
	 * ファイルパスの設定。 サンプルなのでポスをベタで指定。
	 */
	private final static String FILE_PATH = ファイルパスを明記;
	private static final long serialVersionUID = 1L;
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 出力ファイルのフルパス
		File fileOut = new File(FILE_PATH);
		printOutFile(request, response, fileOut);
	}
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
	protected void printOutFile(HttpServletRequest request,
			HttpServletResponse response, File fileOut)
			throws ServletException, IOException {
		OutputStream os = response.getOutputStream();
		try {
			FileInputStream hFile = new FileInputStream(FILE_PATH);
			BufferedInputStream bis = new BufferedInputStream(hFile);
			System.out.println("@@@@@ " + fileOut.getName());
			// ヘッダー情報設定
			configureHeaderInfo(fileOut, request, response);
			int length = 0;
                        length = bis.read(buffer);
                        while(length >= 0) {
				os.write(buffer, 0, length);
			}
			byte[] buffer = new byte[1024];
			
			bis.close();
		} catch (IOException e) {
			printOutNotFound(response);
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
				} finally {
					os = null;
				}
			}
		}
	}
	/**
	 * ダウンロードに失敗した場合の処理.
	 * 
	 * @param res
	 *            {@link HttpServletResponse}オブジェクト
	 */
	private void printOutNotFound(HttpServletResponse res) {
		try {
			OutputStream toClient = res.getOutputStream();
			res.setContentType("text/html;charset=utf-8");
			toClient.write("File not found".getBytes());
			toClient.close();
		} catch (IOException e) {
			// do nothing
		}
	}
	/**
	 * ヘッダーに付加するダウンロードファイル名を設定.
	 * 
	 * @param file
	 *            ダウンロード対象の{@link File}オブジェクト
	 * @param response
	 *            {@link HttpServletResponse}オブジェクト
	 * @throws UnsupportedEncodingException
	 *             サポート外の文字コードでエンコードされた際例外発生
	 */
	private void configureHeaderInfo(File file, HttpServletRequest request,
			HttpServletResponse response) throws UnsupportedEncodingException {
		// レスポンス設定
		response.setContentType("application/octet-stream");
		// User-Agent取得
		String userAgent = request.getHeader("User-Agent").toUpperCase();
		System.out.println("UserAgetn -> " + userAgent);
		String fileName = "";
		if (userAgent.indexOf("MSIE") > -1) { // IEの場合
			fileName = URLEncoder.encode(file.getName(), "UTF-8");
			System.out.println("FileName(URLEncoder.encode) -> " + fileName);
			response.setHeader("Content-Disposition", "attachment; filename="
					+ fileName);
		} else if (userAgent.indexOf("SAFARI") > -1
				|| userAgent.indexOf("MAC") > -1) { // Safari、OSがMacの場合
			/*
			 * Safariは苦戦をした。何をしてもファイル名が文字化けていた。
			 * fileOut.getName()で取得したファイル名をそのまま渡しても化けた。なのでエンコードしてももちろん化ける。
			 * エンコード前の状態を渡すと化ける。じゃー化けた状態の物を渡せば正しく解釈されるのでは?と仮定しString,getByteを行う。
			 * バイトに変換したStringを渡すことで解決。
			 */
			fileName = new String(file.getName().getBytes("UTF-8"), "8859_1");
			System.out.println("FileName(String.getBytes) -> " + fileName);
			response.setHeader("Content-Disposition", "attachment; filename="
					+ fileName);
		} else { // 上以外
			fileName = MimeUtility.encodeWord(fileName, "ISO-2022-JP", "B");
			System.out.println("FileName(MimeUtility.encodeWord) -> "
					+ fileName);
			response.setHeader("Content-Disposition", "attachment; filename="
					+ fileName);
		}
	}
}
 
	





Leave a reply