前回作った、CSVファイルのダウンロードサンプルでしたが、このままでは日本語ファイル名を扱えないのが分かったので、日本語ファイル名にも対応出来るサーブレットのサンプルを作成しました。

■目次


ファイルの配置

/CONTEXT_ROOT
┣ WEB-INF
┃┣ src
┃┃┗ DynamicCsvJpServlet.java
┃┗ web.xml
┗ jsp
 ┗ dynamic_csv_jp.jsp

日本語対応サンプルサーブレット(DynamicCsvJpServlet.java)

import java.io.*;
import java.net.URLEncoder;
 
import javax.servlet.*;
import javax.servlet.http.*;
 
import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeUtility;
 
public class DynamicCsvJpServlet extends HttpServlet {
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
      IOException {
    // POSTメソッドでアクセスが来てもGETメソッドで全部処理します。
    doGet(req, resp);
  }
 
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
      IOException {
 
    // CSV出力用データ
    String[][] rows = {
        { "0", "ダイナミック", "dynamic@example.com" },
        { "1", "ホゲ", "hoge@example.com" },
        { "2", "モゲ", "moge@example.com" },
        { "3", "マゲ", "mage@example.com" },
        { "4", "フゥ", "foo@example.com" },
        { "5", "バァ", "bar@example.com" },
        { "6", "グゥ", "goo@example.com" }
        };
 
 
    // ファイル名設定
    // リクエストパスからファイル名を取得
    String fileName = new String(req.getPathInfo().substring(1).getBytes("ISO-8859-1"), "UTF-8");
    // リクエストヘッダからユーザーエージェント(ブラウザの名前)取得
    String ua = req.getHeader("user-agent");
    if (ua.contains("MSIE")) {
      // IE用ファイル名エンコード
      fileName = URLEncoder.encode(fileName, "UTF-8");
    } else {
      // IE以外(FireFox,Safari,Chrome)ファイル名エンコード
      fileName = MimeUtility.encodeWord(fileName, "ISO-2022-JP", "B");
    }
 
    // レスポンスヘッダー設定
    if (ua.contains("Safari")) {
      // サファリ用
      resp.setContentType("application/octet-stream;");
    } else {
      // サファリ以外用
      resp.setContentType("text/csv; charset=UTF-8;");
      resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
    }
 
    // CSVデータ作成
    StringBuffer sb = new StringBuffer();
    for (String[] row : rows) {
      for (int i = 0; i < row.length; i++) {
        if (i == 0) {
          sb.append("\"");
        } else {
          sb.append("\",\"");
        }
        sb.append(row[i]);
 
        if (i == row.length - 1) {
          sb.append("\"\n");
        }
      }
    }
 
    // レスポンスにCSV出力
    PrintWriter w = resp.getWriter();
    w.print(sb.toString());
    w.flush();
  }
}
 
  • 前回作ったCSV出力サーブレットを、日本語ファイル名に対応出来る用に修正しました。

  • 修正箇所説明
// リクエストパスからファイル名を取得
String fileName = new String(req.getPathInfo().substring(1).getBytes("ISO-8859-1"), "UTF-8");
  • まずここで、リクエストパスからファイル名を取得します。
    • 例:「/hogeservlet/hoge.csv」にアクセス → リクエストパスに「/hoge.csv」が入る → 1文字目をカットして「hoge.csv」を取得
  • リクエストパスに日本語が入ると文字化けしてしまうので、文字化け対策として一度バイト配列に戻し、UTF-8で文字列に戻しています。

// リクエストヘッダからユーザーエージェント(ブラウザの名前)取得
String ua = req.getHeader("user-agent");
if (ua.contains("MSIE")) {
  // IE用ファイル名エンコード
  fileName = URLEncoder.encode(fileName, "UTF-8");
} else {
  // IE以外(FireFox,Safari,Chrome)ファイル名エンコード
  fileName = MimeUtility.encodeWord(fileName, "ISO-2022-JP", "B");
}
 
  • ここでは、ブラウザ毎の動作の違いを吸収します。
  • IEは日本語ファイル名は「URLEncoder.encode」を使いエンコードして上げる必要があります。
  • FireFox Chromeは「MimeUtility.encodeWord」を使います。

// レスポンスヘッダー設定
if (ua.contains("Safari")) {
  // サファリ用
  resp.setContentType("application/octet-stream;");
} else {
  // サファリ以外用
  resp.setContentType("text/csv; charset=UTF-8;");
  resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
}
 
  • ここでもブラウザ毎の動作の違いを吸収しています。
  • まず、サファリでは日本語のファイル名を「Content-Disposition」で上手く設定出来ませんでした。
    • そこでサファリ用に、リクエストパスにファイル名を持たせて、サファリの場合はコンテンツタイプを「application/octet-stream」するという方法にしました。
    • この対応でなんとか、ウィンドウズ7のサファリですが日本語のファイル名でダウンロードできるようになりました。
  • サファリ以外は、前回とほぼ変わらずIE用とそれ以外用にエンコードしたファイル名を設定しています。

web.xmlの設定

<?xml version="1.0" encoding="ISO-8859-1"?>
 
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0">
 
  <!-- サーブレットの宣言 -->
  <servlet>
    <servlet-name>dynamiccsvjpservlet</servlet-name>
    <servlet-class>DynamicCsvJpServlet</servlet-class>
  </servlet>
 
  <!-- サーブレットとurlのマッピング -->
  <servlet-mapping>
    <servlet-name>dynamiccsvjpservlet</servlet-name>
    <url-pattern>/dynamiccsvjpservlet/*</url-pattern>
  </servlet-mapping>
</web-app>
 
  • サーブレットの宣言とURLのマッピングです。
  • 「url-pattern」にワイルドカードを設定して、「/dynamiccsvjpservlet/日本語.csv」のようにアクセスすると、「DynamicCsvJpServlet」が実行され、*の部分に相当する「/日本語.csv」をサーブレットで取得出来るのです。
  • この時点で、Tomcatを起動して「http://localhost:8080/CONTEXT_ROOT/dynamiccsvjpservlet/日本語.csv」にアクセスすると、CSVファイルのダウンロードダイアログが出てくると思います。


サンプルサーブレットへのリンクを表示するJSP(dynamic_csv_jp.jsp)

<%@ page language="java" contentType="text/html; charset=UTF8" pageEncoding="UTF-8" %>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
  </head>
  <body>
    <a href='<%=request.getContextPath()+"/dynamiccsvjpservlet/" +
  java.net.URLEncoder.encode("日本語.csv","UTF-8")%>'>エンコードあり</a><br>
    <a href='<%=request.getContextPath()+"/dynamiccsvjpservlet/日本語.csv"%>'>エンコードなし</a><br>
  </body>
</html>
 
  • 日本語対応のサーブレットにアクセスするリンクを表示するJSPです。サーブレットのパスに続けてCSVのファイル名もURLに含めてあります。
  • URLエンコードありとなし両方用意してありますが、どちらでも問題なくアクセス出来ます。
  • アクセス出来ないブラウザがないとも限らないので、エンコードしておいたほうがよいと思いますが。

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
  </head>
  <body>
    <a href='/Tomcat7Test/dynamiccsvjpservlet/%E6%97%A5%E6%9C%AC%E8%AA%9E.csv'>エンコードあり</a><br>
    <a href='/Tomcat7Test/dynamiccsvjpservlet/日本語.csv'>エンコードなし</a><br>
  </body>
</html>
 
  • JSPが出力したHTMLです。
  • ファイル名エンコードのありとなしがはっきり分かりますが、どちらでも問題なくアクセス出来ました。


ブラウザでアクセス

http://localhost:8080/CONTEXT_ROOT/jsp/dynamic_csv_jp.jsp」にアクセスすると、ダウンロードのリンクが表示されて、リンククリックでブラウザのダウンロード処理が動きます。

他のTomcatサンプルはこちら


コメント

名前: