【Angular + Spring Boot】ファイルのダウンロードを実装する

バックエンド ( Spring Boot ) からPDFファイルをレスポンスとして返し、フロントエンド ( Angular ) へダウンロードするという動きを作っていきます。

Angular + Spring Boot でファイルのダウンロードを実装する

今回使用した環境

インターネット接続可能のオンラインの環境

64 ビット オペレーティング システム

Windows 10 22H2

Angular CLI: 13.3.10

Node: 16.15.0

Spring Boot: 3.0.0

Java: 17.0.3.1

ソース ( バックエンド )

バックエンド側では「test.pdf」をレスポンスとして返しています。

import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import jakarta.servlet.http.HttpServletResponse;

@Controller
public class FileDownloadController {

    @GetMapping(value ="download-test")
    public void download(HttpServletResponse response) {
        
        try (OutputStream outputStream = response.getOutputStream();) {
            
            //ダウンロードファイルをバイト配列に変換
            Path path = Paths.get("D:/work/test.pdf");
            byte[] fileByte = Files.readAllBytes(path);

            //レスポンスヘッダに値をセット 
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "attachment; filename=test.pdf");
            response.setContentLength(fileByte.length);
            outputStream.write(fileByte);
            outputStream.flush();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上記コードはSpringBoot ファイルをダウンロードするサンプルのサイトを参考にさせていただきました。

ソース ( フロントエンド )

html側です。

ダウンロードボタンをクリックするとバックエンド側へリクエストを送信し、レスポンスのpdfファイルをaタグに埋め込んでそれをダウンロードする。

という流れです。このやり方が良いやり方なのかどうかはわかりませんが…。

<p>download-test works!</p>
<button (click)="download()">ダウンロード</button>

<div style="display: none;">
    <a #downloadtag [href]="safeResourceUrl" target="_blank" 
       download="test.pdf">Download</a>
</div>

TypeScript側です。

import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';

@Component({
  selector: 'app-download-test',
  templateUrl: './download-test.component.html',
  styleUrls: ['./download-test.component.css']
})
export class DownloadTestComponent implements OnInit {

  safeResourceUrl: SafeResourceUrl;
  @ViewChild('downloadtag') downloadtag: ElementRef;

  constructor(
    private http: HttpClient,
    private sanitizer: DomSanitizer,
    private changeDetectorRef: ChangeDetectorRef,
  ) { }

  ngOnInit(): void {
  }

  download(): void {
    this.http.get("http://localhost:8080/download-test", 
                  {responseType: "blob"}).subscribe((blob) => {
      this.safeResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl(
                                            URL.createObjectURL(blob));
      const downloadtag = <HTMLLinkElement>(this.downloadtag.nativeElement);
      this.changeDetectorRef.detectChanges();
      downloadtag.click();
    })
  }
}

25行目でバックエンドへのリクエストを行っています。

26行目の"blob"とはバイナリでレスポンスが返ってきたことを意味しています。

30行目は強制的に#downloadtagへsafeResourceUrlの値をバインディングしています。※これをしないとバインディング前にダウンロードが実行されてしまい、不正なファイルとなるケースがありました。

動作確認

ダウンロードボタンをクリックすることでtest.pdfがダウンロードできました。

※今回は CORS ( クロスオリジン ) の問題はブラウザ起動時のオプションで無効にしています。

以上となります。

ここまでお読みいただきありがとうございました。

Angular

Posted by だゆう