8
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SpringのRestTemplateで巨大ファイルダウンロードを実現する方法

Posted at

1. はじめに

今回はSpringFrameworkRestTemplateを利用して巨大ファイルをダウンロードする方法について説明します。ポイントはResponseExtractorを利用することです。
なお、巨大ファイルダウンロードのサーバ側の実装については「TERASOLUNA5.x(=SpringMVC)で巨大ファイルダウンロードを実現する方法」を参照ください。

2. ソースコード

LargeFileResponseExtractor.java
package todo.app.largefile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.client.ResponseExtractor;

/**
 * ★ポイント1
 * Implements ResponseExtractor class for large file
 */
public class LargeFileResponseExtractor implements
                                        ResponseExtractor<ResponseEntity<File>> {

    /**
     * ファイルパス
     */
    private String filePath;

    /**
     * 空のコンストラクタ
     */
    public LargeFileResponseExtractor() {

    }

    /**
     * コンストラクタ
     * @param filePath ファイルパス
     */
    public LargeFileResponseExtractor(String filePath) {
        this.filePath = filePath;
    }

    /**
     * ★ポイント2
     * @see org.springframework.web.client.ResponseExtractor#extractData(org.springframework.http.client.ClientHttpResponse)
     */
    @Override
    public ResponseEntity<File> extractData(
            ClientHttpResponse response) throws IOException {

        // 1. create File object on instructed file path or temporary
        File downloadFile = null;
        if (filePath == null) {
            downloadFile = File.createTempFile("download", null);
        } else {
            downloadFile = new File(filePath);
        }

        // 2. copy contents with buffering
        FileCopyUtils.copy(response.getBody(),
                new FileOutputStream(downloadFile));

        // ★ポイント3
        // 3. create ResponseEntity object with status, headers, body
        return ResponseEntity.status(response.getStatusCode())
                .headers(response.getHeaders()).body(downloadFile);
    }
}

★ポイント1
org.springframework.web.client.ResponseExtractorインターフェースを実装したクラスを定義します。
<>にはResponseExtractorの戻り値となるデータ型を定義しますが、ResponseEntity<File>とするのがポイントです。
今回は他でも利用できるように別クラスとして定義しましたが、利用する箇所に①アロー演算子(ラムダ式)、②インナークラス(匿名クラス)として書くことも可能です。

★ポイント2
HTTPレスポンスのBODYからデータをバッファリングしつつ、ファイルとして保存する処理をextractDataメソッドに実装します。この処理により巨大なファイルでもOutOfMemoryにならずにファイルをダウンロードすることが可能になります。

★ポイント3
戻り値となるResponseEntityクラスのオブジェクトを生成します。この際、HTTPレスポンスのステータスコード、レスポンスヘッダも含めるようにします。

FileDownloadControllerTest.java
package todo.app.largefile;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;

import java.io.File;
import java.util.List;

import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;

public class FileDownloadControllerTest {

    @Test
    public void test01() {
        String targetUrl = "http://localhost:8090/todo-rest/download/chunked";

        RestTemplate restTemplate = new RestTemplate();

        // ★ポイント4
        ResponseExtractor<ResponseEntity<File>> responseExtractor = new LargeFileResponseExtractor();

        // ★ポイント5
        ResponseEntity<File> responseEntity = restTemplate.execute(targetUrl,
                HttpMethod.GET, null, responseExtractor);

        // ★ポイント6
        File downloadFile = responseEntity.getBody();
        
        // assert
        HttpHeaders httpHeaders = responseEntity.getHeaders();
        long contentLength = httpHeaders.getContentLength();
        MediaType contentType = httpHeaders.getContentType();
        List<String> checkSum = httpHeaders.get("X-SHA1-CheckSum");
        // ★ポイント7
        assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
        assertThat(contentLength, is(downloadFile.length()));
        // omitted

    }
}

★ポイント4
LargeFileResponseExtractorクラスのオブジェクトを生成します。

★ポイント5
RestTemplateexecuteメソッドでHTTPリクエストを発行します。この際、第4引数のResponseExtractorに★ポイント4で生成したLargeFileResponseExtractorのオブジェクトを指定します。

★ポイント6
ResponseEntitygetBodyメソッドを実行するとLargeFileResponseExtractorで保存したダウンロードファイルが取得できます。

★ポイント7
今回のサンプルではContent-Lengthヘッダのデータサイズとダウンロードファイルのデータサイズが等しいことを確認しています。
TERASOLUNA5.x(=SpringMVC)で巨大ファイルダウンロードを実現する方法」のサーバ処理ではダウンロードファイルのチェックサム(ハッシュ値)をX-SHA1-CheckSumヘッダに設定しています。今回は省略しましたが、このチェックサムを比較して正常にダウンロードできたか確認する処理を追加してもいいでしょう。

3. さいごに

今回はRestTemplateResponseExtractorを利用して巨大ファイルをダウンロードする方法について説明しました。
今回の方法はJdbcTemplateResultSetExtractorを利用する方法とパターンが似ているかと思います。一般的にフレームワーク内ではアーキテクチャを揃えるため、APIの使い方は似たような感じになります。一つのAPIの使い方を覚えたら他も似たような感じになると思って調べてみるとよいかと思います。

8
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?