Edited at

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

More than 1 year has passed since last update.


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

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?