Spring Boot DB連携

Spring BootのWEB画面上でPDFプレビューを実装してみた

前回は、Sping BootのWEB画面上で、ファイルアップロード・ファイルダウンロード機能について記載したが、今回は、PDFの場合のみPDFプレビューを行うようにしたので、そのサンプルプログラムを共有する。

前提条件

下記記事の実装が完了していること。

Spring BootのWEB画面上でファイルアップロード・ダウンロード機能を実装してみた(ソースコード編)今回も引き続き、ファイルアップロード・ダウンロード機能の実装について述べる。ここでは、具体的なサンプルプログラムのソースコードを共有する...

完成イメージ

ここでは、完成した画面イメージの共有を行う。

Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスした場合の初期表示は以下の通りとする。
画面遷移1

「PDFプレビュー」ボタンを押下すると、以下のように、別画面にPDFプレビュー画面が表示される。
画面遷移2

テスト.txtをダウンロードした結果は以下の通り
画面遷移_3-1

画面遷移_3-2 画面遷移_3-3

また、背景画像.jpgをダウンロードした結果は以下の通り
画面遷移_4-1

画面遷移_4-2 画面遷移_4-3



作成したサンプルプログラムの内容

作成したサンプルプログラムの構成は以下の通り。なお、下図の赤枠は、前提条件に記載した記事と変更になったソースコードを示しており、今後記載する。
サンプルプログラムの構成

一覧画面を表示するプログラムは以下の通り。PDFの場合は「PDFプレビュー」ボタンを、それ以外の場合は「ダウンロード」ボタンを表示するようにしている。PDFの場合は、window.open関数を呼んでいて、新規画面を開くようにしている。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>一覧画面</title>
</head>
<body>
ファイルデータテーブル(file_data)の全データ<br/><br/>

<table border="1" cellpadding="5">
    <tr>
        <th>ID</th>
        <th>ファイルパス</th>
        <th></th>
    </tr>
    <tr th:each="obj : ${fileDataList}">
        <td th:text="${obj.id}"></td>
        <td th:text="${obj.filePath}"></td>
        <td>
            <!-- ダウンロードボタンを表示 -->
            <!-- PDFファイルの場合 -->
            <div th:if=${#strings.endsWith(obj.filePath,'.pdf')}>
                <input type="button" value="PDFプレビュー"
                       th:onclick="'window.open(\'/download?id='
                             + ${obj.id} + '\', target=\'_blank\');'" />
            </div>
            <!-- PDFファイル以外の場合 -->
            <div th:unless=${#strings.endsWith(obj.filePath,'.pdf')}>
                <form action="#" method="get"
                      th:action="@{/download(id=${'__${obj.id}__'})}"
                      th:method="download" >
                    <input type="hidden" name="_method" value="download" />
                    <input type="submit" value="ダウンロード" />
                </form>
            </div>
        </td>
    </tr>
</table>
<br/><br/>
<form method="post" th:action="@{/to_add}">
    <input type="submit" value="データ追加" /><br/><br/>
    <input type="button" value="閉じる" onclick="window.close();" />
</form>
</body>
</html>



さらに、ダウンロード処理を含むコントローラクラスのプログラムは以下の通り。downloadメソッドが変更点である。PDFとそれ以外で処理を分け、PDFの場合は、ContentTypeを「application/pdf」に設定し「Content-Disposition」をinlineにすることで、(新しく開いた画面に)PDFプレビュー画面を開くようにしている。

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;

@Controller
public class DemoController {

    /**
     * ファイルデータテーブル(file_data)へアクセスするMapper
     */
    @Autowired
    private FileDataMapper fileDataMapper;

    /**
     * ファイルデータ一覧表示処理
     * @param model Modelオブジェクト
     * @return 一覧画面
     */
    @RequestMapping("/")
    public String index(Model model){
        //ファイルデータテーブル(file_data)を全件取得
        List<FileData> list = fileDataMapper.findAll();
        model.addAttribute("fileDataList", list);

        //一覧画面へ移動
        return "list";
    }

    /**
     * ファイルデータ登録画面への遷移処理
     * @return ファイルデータ登録画面
     */
    @PostMapping("/to_add")
    public String to_add(){
        return "add";
    }

    /**
     * ファイルデータ登録処理
     * @param uploadFile アップロードファイル
     * @return ファイルデータ一覧表示処理
     */
    @PostMapping("/add")
    @Transactional(readOnly = false)
    public String add(@RequestParam("upload_file") MultipartFile uploadFile){
        //最大値IDを取得
        long maxId = fileDataMapper.getMaxId();

        //追加するデータを作成
        FileData fileData = new FileData();
        fileData.setId(maxId + 1);
        fileData.setFilePath(uploadFile.getOriginalFilename());
        try{
            fileData.setFileObj(uploadFile.getInputStream());
        }catch(Exception e){
            System.err.println(e);
        }
        //1件追加
        fileDataMapper.insert(fileData);

        //一覧画面へ遷移
        return "redirect:/to_index";
    }

    /**
     * 追加完了後に一覧画面に戻る
     * @param model Modelオブジェクト
     * @return 一覧画面
     */
    @GetMapping("/to_index")
    public String toIndex(Model model){
        return index(model);
    }

    /**
     * ファイルダウンロード処理
     * @param id ID
     * @param response HttpServletResponse
     * @return 画面遷移先
     */
    @RequestMapping("/download")
    public String download(@RequestParam("id") String id
            , HttpServletResponse response){
        //ダウンロード対象のファイルデータを取得
        FileData data = fileDataMapper.findById(Long.parseLong(id));

        //ダウンロード対象のファイルデータがnullの場合はエラー画面に遷移
        if(data == null || data.getFileObj() == null){
            return "download_error";
        }

        //PDFの場合
        if(data.getFilePath().endsWith(".pdf")){
            //PDFプレビューの設定を実施
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "inline;");
        }else{
            //ファイルダウンロードの設定を実施
            //ファイルの種類は指定しない
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition"
                 ,"attachment;filename=\"" + getFileName(data.getFilePath()) + "\"");
        }
        //その他の設定を実施
        response.setHeader("Cache-Control", "private");
        response.setHeader("Pragma", "");

        try(OutputStream out = response.getOutputStream();
            InputStream in = data.getFileObj()){
            byte[] buff = new byte[1024];
            int len = 0;
            while ((len = in.read(buff, 0, buff.length)) != -1) {
                out.write(buff, 0, len);
            }
            out.flush();
        }catch(Exception e){
            System.err.println(e);
        }

        //画面遷移先はnullを指定
        return null;
    }

    /**
     * ファイルパスからファイル名を取得する
     * @param filePath ファイルパス
     * @return ファイル名
     */
    private String getFileName(String filePath){
        String fileName = "";
        if(filePath != null && !"".equals(filePath)){
            try{
                //ファイル名をUTF-8でエンコードして指定
                fileName = URLEncoder.encode(new File(filePath).getName(), "UTF-8");
            }catch(Exception e){
                System.err.println(e);
                return "";
            }
        }
        return fileName;
    }

}

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-pdf-preview/demo

要点まとめ

  • ContentTypeを「application/pdf」に設定し、「Content-Disposition」をinlineにすることで、PDFプレビュー表示が行える。