Java
springframework
spring-mvc

SpringMVCでファイルのダウンロードを実装する

ファイルのダウンロードをしてみた。

XMLファイルとかの場合単にコントローラーに

Controller.java
    @Autowired
    ResourceLoader resourceLoader;

    @RequestMapping(value = "/file_download", produces = MediaType.APPLICATION_XML_VALUE)
    public Resource xmlfile() {

        Resource resource = resourceLoader.getResource("classpath:sample.xml");
       try{
        return new FileSystemResource(resource.getFile().toString());
        } catch (FileNotFoundException e) {
        e.getStackTrace();
        }
    }

とかって書くと行けちゃったりしますが、これだとブラウザに表示されて終わりなので、
やっぱりダウンロードフォルダに欲しいよね。
って事でやり方をメモ

やることの構成

1.ファイルを読み込む
2.バイト形式に変換
3.OutputStreamに書き込む

こんな感じで3つの処理を行います。
最後に適当なビュー(nullでもいいのよ)を返してあげるとダウンロード機能の完成です。

1.ファイルを読み込む

まずはファイルをセットしましょう。
src/main/resourcesにファイルをセットします。

image.png

今回はこんなsample.xmlをセット

ファイルを読み込むコントローラーの部分を作ります。

XMLController.java
@RestController
public class XMLController {

    @Autowired
    ResourceLoader resourceLoader;

    @RequestMapping(value="/personize", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public String personize(HttpServletResponse response) {

        Resource resource = resourceLoader.getResource("classpath:sample.xml");
        }
}

ファイルの読み込みはこれだけでOKです。ResourceLoaderはコンテキスト生成時に自動でDIコンテナに登録される(っぽい)のでxmlファイルに記述する必要すらありません。ありがたく@Autowiredしましょう。

2.バイト形式に変換

処理を分けましょう。
新しいプライベートメソッドを作ります。

XMLController.java
    /**
     * InputStream から バイト文字列に変換
     * @param filepath
     * @return
     */
    private byte[] StreamToByte(Resource resource) {

    int nRead;
    InputStream is = null;
    byte[] fileContent = new byte[16384];
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    //ファイルをバイト形式に変換
    try {
        is = new FileInputStream(resource.getFile().toString());

        while ((nRead = is.read(fileContent, 0, fileContent.length)) != -1) {
              buffer.write(fileContent, 0, nRead);
            }

            buffer.flush();

            return buffer.toByteArray();
    } catch (FileNotFoundException e) {
        e.getStackTrace();
    } catch (IOException e) {
        // TODO 自動生成された catch ブロック
        e.printStackTrace();
    }
    return null;
    }

Springとしてやっていることは15行目のresource.getFile().toString()の部分だけです。
これはgetFile()メソッドで、src/main/resource下にあるファイルをFile形式で取得できます。それをtoString()して絶対アドレスに変換するという肯定ですね。

まぁ、せっかくなのでJavaコードの解説も

XMLController.java
        while ((nRead = is.read(fileContent, 0, fileContent.length)) != -1) {
              buffer.write(fileContent, 0, nRead);
            }

        buffer.flush();

ここがBufferdOutputStreamに書き込んでいるメインの部分です。バッファにデータを貯めてフラッシュで実行してます。
Cっぽいですけど、もう定型文ですね。
後はtry~catchでエクセプションを取得しています。

3.OutputStreamに書き込む

responseのアウトプットストリームに書き込みます
まずはどんな種類のファイルか、ヘッダーをセットしましょう。

XMLController.java
    @RequestMapping(value="/personize", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public String personize(HttpServletResponse response) {

        Resource resource = resourceLoader.getResource("classpath:sample.xml");

        byte[] fileContent = null;

        fileContent = StreamToByte(resource);

//      //ファイル書き込み
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + "sample.xml");
        response.setContentLength(fileContent.length);

       }

setContentTypeではバイナリファイルという型を指定してます。response.setHeaderでファイルネームを指定していますが、これが実際にダウンロードされるファイルの名前になります。response.setContentLengthはファイルの大きさですね。

それではOutputStreamに作成してたバイナリを書き込みましょう
これも別メソッドに書きます。

XMLController.java
    /**
     * ダウンロードファイル書き込み
     * @param response
     * @param fileContent
     */
    public void OutputSreamWrite(HttpServletResponse response, byte[] fileContent) {
        OutputStream os = null;
        try {
            os = response.getOutputStream();
            os.write(fileContent);
            os.flush();
        } catch (IOException e) {
            e.getStackTrace();
        }
    }

特に難しい所はないでしょう。os.writeで書き込み、os.flushでストリームを実行です。

このメソッドをコントローラーに組み込みます。

XMLController.java
    @RequestMapping(value="/personize", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public String personize(HttpServletResponse response) {

        Resource resource = resourceLoader.getResource("classpath:sample.xml");

        byte[] fileContent = null;

        fileContent = StreamToByte(resource);

//      //ファイル書き込み
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + "sample.xml");
        response.setContentLength(fileContent.length);

        OutputSreamWrite(response, fileContent);

        return null;
    }

という感じで、コントローラーの最終形はこうなります

XMLController.java
package tsugaruinfo.controller;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class XMLController {

    @Autowired
    ResourceLoader resourceLoader;

    DomController(){

    }

    @RequestMapping(value="/personize", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public String personize(HttpServletResponse response) {

        Resource resource = resourceLoader.getResource("classpath:sample.xml");

        byte[] fileContent = null;

        fileContent = StreamToByte(resource);

//      //ファイル書き込み
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + "sample.xml");
        response.setContentLength(fileContent.length);

        OutputSreamWrite(response, fileContent);

        return null;
    }

    /**
     * InputStream から バイト文字列に変換
     * @param filepath
     * @return
     */
    private byte[] StreamToByte(Resource resource) {

    int nRead;
    InputStream is = null;
    byte[] fileContent = new byte[16384];
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    //ファイルをバイト形式に変換
    try {
        is = new FileInputStream(resource.getFile().toString());

        while ((nRead = is.read(fileContent, 0, fileContent.length)) != -1) {
              buffer.write(fileContent, 0, nRead);
            }

            buffer.flush();

            return buffer.toByteArray();
    } catch (FileNotFoundException e) {
        e.getStackTrace();
    } catch (IOException e) {
        // TODO 自動生成された catch ブロック
        e.printStackTrace();
    }
    return null;
    }

    /**
     * ダウンロードファイル書き込み
     * @param response
     * @param fileContent
     */
    public void OutputSreamWrite(HttpServletResponse response, byte[] fileContent) {
        OutputStream os = null;
        try {
            os = response.getOutputStream();
            os.write(fileContent);
            os.flush();
        } catch (IOException e) {
            e.getStackTrace();
        }
    }
}


ちょっと長くなっちゃいましたね。ですが、面倒な処理は分離しているので、これから新しいダウンロード機能を作るときは、面倒な処理を使いまわせるようにしています。
1つ1つの処理の解説はしているので、全体を一気に取らえるより、小さな完成品を理解するようにすればいいかと。

レッツ実行

では実際のアドレスにアクセスしてみましょう。

image.png

image.png

sample.xmlがサーバーからダウンロードできました。

参考

http://bookmount8.blog.fc2.com/blog-entry-49.html?