コンピュータクワガタ

かっぱのかっぱによるコンピュータ関連のサイトです

Spring MVC 4.1 No. 027 リクエストスコープにデータを格納

Springではいくつかの方法でリクエストスコープにデータを格納できます。ひとつは通常のサーブレットと同様にHttpServletRequestを使う方法。2つ目がWebRequestを使う方法。3つ目がModelを使う方法になります。

package com.example.spring.controller.c027;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;

@Controller
public class C027Controller {
    @RequestMapping("/c027/requestScope")
    public String requestScope(HttpServletRequest request,
            WebRequest webRequest, Model model) {
        request.setAttribute("req1", "httpServletRequest");
        webRequest.setAttribute("req2", "webRequest", WebRequest.SCOPE_REQUEST);
        model.addAttribute("req3", "model");
        return "c027/requestScope";
    }
}

WebRequestを使用する場合には、格納するスコープを3番目の引数に指定します。それ以外はHttpServletRequestと同様です。

Modelオブジェクトを利用する場合は、メソッド名が違うだけでHttpServletRequestと同様です。

使用するJSPでは、明示してrequestScopeから値を取得しています。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
HttpServletRequest: <c:out value="${requestScope.req1}" /><br>
WebRequest: <c:out value="${requestScope.req2}" /><br>
Model: <c:out value="${requestScope.req3}" />
 </body>
</html>

確認用のテストケースは次のとおりです。

package com.example.spring.controller.c027;

import static org.hamcrest.CoreMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/spring-context.xml" })
public class C027ControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(wac).build();
    }

    @Test
    public void requestScopeのGET() throws Exception {
        mockMvc.perform(get("/c027/requestScope"))
                .andExpect(status().isOk())
                .andExpect(view().name("c027/requestScope"))
                .andExpect(
                        request().attribute("req1", is("httpServletRequest")))
                .andExpect(request().attribute("req2", is("webRequest")))
                .andExpect(model().attribute("req3", is("model")));
    }
}

ソースは https://github.com/kuwalab/spring-mvc41 にあります。タグ027が今回のサンプルです。

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初 http://kuwalab.hatenablog.jp/entry/spring_mvc41/001
前回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/026
次回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/028

Spring MVC 4.1 No. 026 ファイルのアップロードの例外処理

ファイルアップロード時の例外(アップロードサイズが大きすぎる等)処理はやや煩雑です。また、ファイルアップロード時の上限ファイルサイズをきちんと決めておかないと、サーバーのリソースを食いつぶす原因にもなります。

まず、アップロード時の上限サイズをweb.xmlに記載します。

<multipart-config>
 <location>/tmp</location>
 <max-file-size>1000000</max-file-size>
 <max-request-size>1100000</max-request-size>
 <file-size-threshold>0</file-size-threshold>
</multipart-config>

この例ではは約1MBを上限としています。

また、正常に例外処理をするためにMultipartFilterを設定します。

<filter>
 <filter-name>MultipartFilter</filter-name>
 <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>MultipartFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

この設定でファイルのアップロードのコントローラーを次のように作成します。

package com.example.spring.controller.c026;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/c026")
public class C026Controller {
    @RequestMapping("/uploadForm")
    public String uploadForm(Model model) {
        System.out.println("#########");
        return "c026/uploadForm";
    }

    @RequestMapping(value = "/uploadRecv", method = RequestMethod.POST)
    public String uploadRecv(@RequestParam String test,
            @RequestParam MultipartFile file, Model model) throws IOException {
        model.addAttribute("test", test);
        Path path = Paths.get(System.getProperty("java.io.tmpdir"),
                file.getOriginalFilename());
        file.transferTo(path.toFile());
        model.addAttribute("fileName", path);

        return "c026/uploadRecv";
    }

    @RequestMapping(value = "/error")
    public String error(HttpServletRequest request, Model model) {
        String uploadUrl = request.getParameter("uploadUrl");
        model.addAttribute("errorMessage", "ファイルサイズが大きすぎます");
        return "forward:" + uploadUrl;
    }
}

今回は、エラーのハンドリング用のerrorメソッドを用意しています。ファイルアップロードの例外が発生した際にこのメソッドが呼ばれるように設定します。errorメソッドでは遷移先のURLをリクエストから取得し、そこにフォワードしつつ、エラーメッセージをセットしています。

アップロードするためのuploadForm.jspは次のとおりです。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
  <c:out value="${errorMessage}" /><br>
  <form action="uploadRecv?uploadUrl=/c026/uploadForm" method="post" enctype="multipart/form-data">
   <input type="text" name="test"><br>
   <input type="file" name="file"><br>
   <input type="submit" value="送信">
  </form>
 </body>
</html>

エラーメッセージの表示と、formのaction属性にアップロード処理のエラー発生時の遷移先URLを付加しています。

アップロードが正常に終了した際の表示用のuploadRecv.jspは次のとおりです。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
アップロードされました。<br>
ファイル名は<c:out value="${fileName}" /><br>
送信されたtestパラメータは<c:out value="${test}" />
 </body>
</html>

Springでファイルアップロードの上限を超えた場合には、org.springframework.web.multipart.MultipartExceptionが発生します。web.xmlでこの例外をハンドリングして、先に作成した/c026/errorに遷移します。

<error-page>
 <exception-type>org.springframework.web.multipart.MultipartException</exception-type>
 <location>/c026/error</location>
</error-page>

ソースは https://github.com/kuwalab/spring-mvc41 にあります。タグ026が今回のサンプルです。

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初 http://kuwalab.hatenablog.jp/entry/spring_mvc41/001
前回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/025
次回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/027

スマホ版ドラクエ5の違い

スマホ版のドラクエが出て10日位経ちました。

私がやっていた中でDS版のドラクエ5との違いを記載していきます。

一番の違いはすごろく場です。特に宝物庫はサイコロの出目により11〜66秒だったのが、倍の22秒〜132秒になっています。そのほか悪い効果(?マスでお金を落とすとか)がぬるくなっているように思います。

また、1月1日までの特典のモンスターブローチのお陰で仲間がなり易いのも大きな違いです。ヘルバトラーを2匹あっさりつもりました。

厳密に確認していませんが、パルプンテも調整されているかもです。

裏ボスまで含めて終わっていますが、わかっている違いはこれくらいです。

Spring MVC 4.1 No. 025 ファイルのアップロード

Java EE 6、Servlet 3.0から標準でファイルのアップロードができるようになりました。今回はServlet 3.0のファイルアップロードを行います。

ファイルアップロードする場合には、web.xmlにアップロードの設定が必要です(もしくはServletアノテーションですが、DispathcerServletなのでweb.xmlになります)。web.xmlのDispatcherServletの設定を次のようにします。

<servlet>
 <servlet-name>dispatcher</servlet-name>
 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 <init-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/spring/spring-context.xml</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
 <multipart-config>
  <location>/tmp</location>
  <max-file-size>1000000</max-file-size>
  <max-request-size>1000000</max-request-size>
  <file-size-threshold>10000</file-size-threshold>
 </multipart-config>
</servlet>

また、Springの設定も必要になります。Serlvet 3.0のファイルアップロードを使用する設定になります。

<bean id="multipartResolver"
 class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>

Controllerは以下のようにします。受け取る際には、@RequestPartアノテーションを付けた引数で受け取ります。

package com.example.spring.controller.c025;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/c025")
public class C025Controller {
    @RequestMapping("/uploadForm")
    public String uploadForm() {
        return "c025/uploadForm";
    }

    @RequestMapping(value = "/uploadRecv", method = RequestMethod.POST)
    public String uploadRecv(@RequestParam String test,
            @RequestParam MultipartFile file, Model model) throws IOException {
        model.addAttribute("test", test);
        Path path = Paths.get(System.getProperty("java.io.tmpdir"),
                file.getOriginalFilename());
        file.transferTo(path.toFile());
        model.addAttribute("fileName", path);

        return "c025/uploadRecv";
    }
}

今回はファイルを一時領域に保管し、そのファイルのパスをtestパラメータと一緒にmodelに格納します。
ファイルを送信するuploadForm.jspです。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
  <form action="uploadRecv" method="post" enctype="multipart/form-data">
   <input type="text" name="test"><br>
   <input type="file" name="file"><br>
   <input type="submit" value="送信">
  </form>
 </body>
</html>

結果表示のuploadRecv.jspです。日本語ファイル名も問題なく表示できます。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
アップロードされました。<br>
ファイル名は<c:out value="${fileName}" /><br>
送信されたtestパラメータは<c:out value="${test}" />
 </body>
</html>

確認用のテストケースは次のとおりです。ファイルアップロードのテストも標準でサポートされています。

package com.example.spring.controller.c025;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;

import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
    "file:src/main/webapp/WEB-INF/spring/spring-context.xml" })
public class C025ControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(wac).build();
    }

    @Test
    public void uploadFormのGET() throws Exception {
        mockMvc.perform(get("/c025/uploadForm")).andExpect(status().isOk())
                .andExpect(view().name("c025/uploadForm"));
    }

    @Test
    public void uploadRecvのPOST() throws Exception {
        byte[] fileImage = null;
        Path path = Paths.get("src/test/resources/c025/kappa.png");
        if (Files.exists(path, LinkOption.NOFOLLOW_LINKS)) {
            fileImage = Files.readAllBytes(path);
        }

        // ローカルのファイル名もエミュレーションできる。
        String fileName = "画像.png";
        MockMultipartFile file = new MockMultipartFile("file", fileName, null,
                fileImage);
        // アップロードされるファイルのパス
        Path actualFile = Paths.get(System.getProperty("java.io.tmpdir"),
                "画像.png");

        mockMvc.perform(
                fileUpload("/c025/uploadRecv").file(file).param("test",
                        "testParam")).andExpect(status().isOk())
                .andExpect(view().name("c025/uploadRecv"))
                .andExpect(model().attribute("test", is("testParam")))
                .andExpect(model().attribute("fileName", actualFile));

        // 画像が保管されていることを確認する
        assertThat(Files.exists(actualFile, LinkOption.NOFOLLOW_LINKS),
                is(true));
        byte[] actualImage = Files.readAllBytes(actualFile);
        assertThat(actualImage, is(equalTo(fileImage)));
        // アップロードされたファイルの削除
        Files.delete(actualFile);
    }
}

ソースは https://github.com/kuwalab/spring-mvc41 にあります。タグ025が今回のサンプルです。

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初 http://kuwalab.hatenablog.jp/entry/spring_mvc41/001
前回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/024
次回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/026

Spring MVC 4.1 No. 024 CSVファイルのダウンロード3

今回もCSVダウンロードについてです。今回はResponseBodyを返す形で実装します。

ResponseEntityではデータと、ヘッダー、ステータスコードを返せます。

日本語の文字化けをしないようにするために、HttpHeadersクラスのsetContentTypeを使わずに、addメソッド文字コードと一緒にcontent-typeを指定しています。

CSVデータはただの文字列のデータになります。

package com.example.spring.controller.c024;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class C024Controller {
    @RequestMapping("/c024/csvInit")
    public String csvInit() {
        return "c024/csvInit";
    }

    @RequestMapping(value = "/c024/csvDown", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE
            + ";charset=utf-8")
    @ResponseBody
    public String csvDown3(HttpServletResponse response) {
        response.setHeader("Content-Disposition",
                "attachment; filename=\"test3.csv\"");

        String csvData = "山田 太郎,33\r\n";
        csvData = csvData + "田中 花子,29\r\n";
        return csvData;
    }
}

アンカーを表示する画面です。単純なアンカーのみです。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
  <a href="csvDown">csvDown</a>
 </body>
</html>

確認用のテストケースは次のとおりです。

package com.example.spring.controller.c024;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/spring-context.xml" })
public class C024ControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(wac).build();
    }

    @Test
    public void csvInitのGET() throws Exception {
        mockMvc.perform(get("/c024/csvInit")).andExpect(status().isOk())
                .andExpect(view().name("c024/csvInit"));
    }

    @Test
    public void csvDownのGET() throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append("山田 太郎,33\r\n");
        sb.append("田中 花子,29\r\n");
        mockMvc.perform(get("/c024/csvDown"))
                .andExpect(status().isOk())
                .andExpect(
                        content().contentType(
                                "application/octet-stream;charset=utf-8"))
                .andExpect(content().string(sb.toString()));
    }
}

ソースは https://github.com/kuwalab/spring-mvc41 にあります。タグ024が今回のサンプルです。

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初 http://kuwalab.hatenablog.jp/entry/spring_mvc41/001
前回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/023
次回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/025

Spring MVC 4.1 No. 023 CSVファイルのダウンロード2

今回もCSVダウンロードについてです。今回はResponseEntityクラスを使用したダウンロードについてです。

ResponseEntityではデータと、ヘッダー、ステータスコードを返せます。

日本語の文字化けをしないようにするために、HttpHeadersクラスのsetContentTypeを使わずに、addメソッド文字コードと一緒にcontent-typeを指定しています。

CSVデータはただの文字列のデータになります。

package com.example.spring.controller.c023;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class C023Controller {
    @RequestMapping("/c023/csvInit")
    public String csvInit() {
        return "c023/csvInit";
    }

    @RequestMapping(value = "/c023/csvDown", method = RequestMethod.GET, produces = "application/octet-stream;charset=utf-8")
    public ResponseEntity<String> csvDown() {
        HttpHeaders headers = new HttpHeaders();
        // headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.add("contet-type", MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE
                + ";utf-8");
        headers.set("Content-Disposition", "filename=\"test2.csv\"");
        String csvData = "山田 太郎,33\r\n";
        csvData = csvData + "田中 花子,29\r\n";

        return new ResponseEntity<String>(csvData, headers, HttpStatus.OK);
    }
}

アンカーを表示する画面です。単純なアンカーのみです。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
  <a href="csvDown">csvDown</a>
 </body>
</html>

確認用のテストケースは次のとおりです。

package com.example.spring.controller.c023;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/spring-context.xml" })
public class C023ControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(wac).build();
    }

    @Test
    public void csvInitのGET() throws Exception {
        mockMvc.perform(get("/c023/csvInit")).andExpect(status().isOk())
                .andExpect(view().name("c023/csvInit"));
    }

    @Test
    public void csvDownのGET() throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append("山田 太郎,33\r\n");
        sb.append("田中 花子,29\r\n");
        mockMvc.perform(get("/c023/csvDown"))
                .andExpect(status().isOk())
                .andExpect(
                        content().contentType(
                                "application/octet-stream;charset=utf-8"))
                .andExpect(content().string(sb.toString()));
    }
}

ソースは https://github.com/kuwalab/spring-mvc41 にあります。タグ023が今回のサンプルです。

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初 http://kuwalab.hatenablog.jp/entry/spring_mvc41/001
前回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/022
次回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/024

Spring MVC 4.1 No. 022 CSVファイルのダウンロード1

今回はCSVダウンロードです。ダウンロードにはいくつか方法があるのですが、今回はSpringにあまり依存しない方法を紹介します。

具体的なソースは以下のなります。csvInitでアンカーの書かれた画面を開き、csvDownが実際のダウンロードになります。ダウンロードの際には戻り値はvoidとして、HttpServletResponse#getWirter()メソッドでダウンロードするデータを書き出します。

package com.example.spring.controller.c022;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class C022Controller {
    @RequestMapping("/c022/csvInit")
    public String csvInit() {
        return "c022/csvInit";
    }

    @RequestMapping("/c022/csvDown")
    public void csvDown(HttpServletResponse response) {
        response.setContentType(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE
                + ";charset=utf-8");
        response.setHeader("Content-Disposition",
                "attachment; filename=\"test.csv\"");
        try (PrintWriter pw = response.getWriter()) {
            pw.print("山田 太郎,33\r\n");
            pw.print("田中 花子,29\r\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

アンカーを表示する画面です。単純なアンカーのみです。

<%@page contentType="text/html; charset=utf-8" %><%--
--%><!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>サンプル</title>
 </head>
 <body>
  <a href="csvDown">csvDown</a>
 </body>
</html>

確認用のテストケースは次のとおりです。

package com.example.spring.controller.c022;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/spring-context.xml" })
public class C022ControllerTest {
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = webAppContextSetup(wac).build();
    }

    @Test
    public void csvInitのGET() throws Exception {
        mockMvc.perform(get("/c022/csvInit")).andExpect(status().isOk())
                .andExpect(view().name("c022/csvInit"));
    }

    @Test
    public void csvDownのGET() throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append("山田 太郎,33\r\n");
        sb.append("田中 花子,29\r\n");
        mockMvc.perform(get("/c022/csvDown"))
                .andExpect(status().isOk())
                .andExpect(
                        content().contentType(
                                "application/octet-stream;charset=utf-8"))
                .andExpect(content().string(sb.toString()));
    }
}

ソースは https://github.com/kuwalab/spring-mvc41 にあります。タグ022が今回のサンプルです。

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初 http://kuwalab.hatenablog.jp/entry/spring_mvc41/001
前回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/021
次回 http://kuwalab.hatenablog.jp/entry/spring_mvc41/023