html5

IndexedDBにblob保存されたPDFファイルを外部アプリに頼らずにJavascriptで表示(前編)

投稿日:2019年6月10日

はじめに

PDFをHTTPダウンロードするとHDDに保存されるか、外部ビューワが起動するかの2択になります。スマホだとHDDに保存されると表示が面倒ですし、外部ビューワが入っていないAndroidなんかだとインストールするかGoogleドキュメントに上げるまで詰みます。

開発しているアプリでのダウンロードの場合は尚更アプリで管理してよ、みたいな圧力が掛かるので、ダウンロードしたらIndexedDBに保存(前半)、PDF.jsで表示(後半)する方法を検証します。

前半は本題の後編に入る前の準備作業になります。

検証環境

検証プロジェクトはionicでサクッと作成していきます。 Cordovaは無しです。

  • Windows 10
  • NodeJS 8.11
  • Ionic 4.1.0
  • Angular 7.2.2

Ionicプロジェクト作成

ionic-cli@5.0.1が入りました。

C:\src\ionic>npm install -g ionic
C:\Program Files\nodejs\ionic -> C:\Program Files\nodejs\node_modules\ionic\bin\ionic
+ ionic@5.0.1
added 140 packages, removed 253 packages, updated 24 packages and moved 2 packages in 69.815s

「pdfdl」という名前でブランクプロジェクトを作成します。

C:\src\ionic>ionic start pdfdl blank
√ Preparing directory .\pdfdl - done!
(node:8004) ExperimentalWarning: The http2 module is an experimental API.
√ Downloading and extracting blank starter - done!

Installing dependencies may take several minutes.

  ──────────────────────────────────────────────────────────────────────────────

         Ionic Advisory, tailored solutions and expert services by Ionic

                             Go to market faster
                    Real-time troubleshooting and guidance
        Custom training, best practices, code and architecture reviews
      Customized strategies for every phase of the development lifecycle

               Learn more: https://ion.link/advisory

  ──────────────────────────────────────────────────────────────────────────────


> npm.cmd i
npm WARN notice [SECURITY] open has the following vulnerability: 1 critical. Go here for more details: https://www.npmjs.com/advisories?search=open&version=6.0.0 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.

    (snip)

出来たプロジェクトにスマホからもアクセスできるよう–address 0.0.0.0でHTTPサーバを起動します。(最近のionic serveはデフォルトlocalhost起動になりました)

C:\src\ionic\pdfdl>ionic serve --address 0.0.0.0
> ng.cmd run app:serve --host=0.0.0.0 --port=8100
[ng] WARNING: This is a simple server for use in testing or debugging Angular applications
[ng] locally. It hasn't been reviewed for security issues.
[ng] Binding this server to an open connection can result in compromising your application or
[ng] computer. Using a different host than the one passed to the "--host" flag might result in
[ng] websocket connection issues. You might need to use "--disableHostCheck" if that's the
[ng] case.
[INFO] Waiting for connectivity with ng...

[INFO] Development server running!

       Local: http://localhost:8100
       External: http://192.168.96.2:8100, http://192.168.0.5:8100

       Use Ctrl+C to quit this process

[INFO] Browser window opened to http://localhost:8100!

   (snip)

ブラウザ でhttp://{ionic serveしたPCのIP}:8100にアクセス出来ることを確認しておきます。

PDFダウンロード、IndexedDB保存を実装

src/app/app.module.tsにHttpClientModuleを追加してHTTP通信出来るようにします。

import { HttpClientModule } from "@angular/common/http";   ← 追加

@NgModule({
    imports: [
        BrowserModule,
        IonicModule.forRoot(),
        AppRoutingModule,
        HttpClientModule    ← 追加
    ],

src/app/home/home.page.htmlを編集。

<ion-header>
    <ion-toolbar color="dark">
        <ion-title>
            PDFブラウザ表示
        </ion-title>
    </ion-toolbar>
</ion-header>

<ion-content>
    <ion-item>
        <ion-input
            placeholder="PDFファイル名"
            [(ngModel)]="pdfFileName"
        ></ion-input>
        <ion-button (click)="getPdf()">PDF取得</ion-button>
    </ion-item>

    <ion-list *ngFor="let pdf of pdfList">
        <ion-item>
            <ion-label>{{ pdf.fileName }}</ion-label>
            <ion-button (click)="showPdf()">表示</ion-button>
        </ion-item>
    </ion-list>
</ion-content>

src/app/home/home.page.tsを編集。IndexedDBへのPDF保存は以下のオブジェクト形式で行います。

{
id: 自動インクリメント,
fileName: PDFファイル名,
content: PDFのblobデータ
}

import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";

@Component({
    selector: "app-home",
    templateUrl: "home.page.html",
    styleUrls: ["home.page.scss"]
})
export class HomePage {
    // 画面のファイル名入力値
    private pdfFileName;

    // 画面のPDFリスト
    private pdfList = [];

    private dbVer: number = 1.0;
    private dbName: string = "testdb";
    private storeName: string = "pdf";

    constructor(private httpClient: HttpClient) {
        this.initIndexedDB();
        this.initScreen();
    }

    // IndexedDB初期作成
    private initIndexedDB() {
        let openRequest = indexedDB.open(this.dbName, this.dbVer);
        openRequest.onupgradeneeded = event => {
            let db = openRequest.result;

            let store = db.createObjectStore(this.storeName, {
                keyPath: "id",
                autoIncrement: true
            });
        };
    }

    // ホーム画面初期化、HTTP取得したPDFをリスト表示
    private initScreen() {
        this.pdfList = [];

        let openRequest = indexedDB.open(this.dbName, this.dbVer);
        openRequest.onsuccess = event => {
            let db = openRequest.result;
            let transaction = db.transaction([this.storeName], "readonly");
            let store = transaction.objectStore(this.storeName);

            let request = store.openCursor();
            request.onsuccess = event => {
                let cursor = request.result;
                if (cursor) {
                    this.pdfList.push(cursor.value);
                    console.log(cursor.value);
                    cursor.continue();
                }
            };
        };
    }

    // assetsに置いたPDFをHTTP GET
    public getPdf() {
        // 画面で指定された名前のPDFをblob形式でHTTP GET
        let pdfRequest = this.httpClient.get("assets/" + this.pdfFileName, {
            responseType: "blob"
        });

        pdfRequest.subscribe(data => {
            let openRequest = indexedDB.open(this.dbName, this.dbVer);
            openRequest.onsuccess = event => {
                let db = openRequest.result;
                let transaction = db.transaction([this.storeName], "readwrite");
                let store = transaction.objectStore(this.storeName);

                // PDFをIndexedDBに保存。サーバロジックが無いのでContent-Dispositionを設定出来ずファイル名が特定できない
                // ファイル名は簡単に画面入力に使われたものを使用
                let request = store.put({
                    fileName: this.pdfFileName,
                    content: data
                });
                //
                request.onsuccess = () => {
                    this.initScreen();
                };
            };
        });
    }
}

実装終わりです。

実行してみる

適当なPDFをどこかから拾ってきて、 sample1.pdfとでも名前を付け、src/assetsに配備します。

ブラウザからsample1.pdfと入力してPDF取得ボタンを押すとHTTPダウンロードしたPDFファイルがリストに追加され、IndexedDBにも保存されます。

表示ボタンは作りましたが処理実装していないので現段階では表示出来ません。保存したPDFを外部ビューワアプリに頼らずに表示する為、後編に続きます。

-html5
-, , ,

執筆者:

関連記事

ionic3(SPA)のServiceWorkerライブラリをsw-toolboxからWorkboxに切り替える

昨今は閲覧速度向上やオフラインでも使える利便性が求められ、Webアプリ開発とPWAはセットで考えられるようになってきましたね。 PWAは「ServiceWorker API」を使ってキャッシュする資源 …

UserAgent判定JSライブラリ「UAParser.js」と「Platform.js」の比較

2019年時点で開発が継続しているUA判定JSライブラリから2つ選択して動作を確認しました。 目次1 Webリソースから比較1.1 github比較1.2 NPMリポジトリ比較2 実際に使用して比較2 …

hidden.inとSoftEtherで無料のビデオ会議環境を構築する

自粛が続いて会えない人も多くなり、人との交流が恋しくなりますね。 仕事ではミーティングをオンライン化するニーズが増え、以下メジャーなパブリックサービスを使っている人も増えていると思います。 Skype …

IndexedDBにblob保存されたPDFファイルを外部アプリに頼らずにJavascriptで表示(後編)

目次1 はじめに2 ビューワ機能を提供してくれるnpmモジュールを選別する3 実装開始3.1 ng2-pdfjs-viewerを使えるようにする3.2 PDFビューワコンポーネントを作る3.3 Hom …

IndexedDBにストアしたオブジェクトのキー値を部分的に更新する

目次1 はじめに2 課題3 より良い方法4 まとめ5 補足 はじめに IndexedDBは「key : value」でレコードを保存するキーバリューストアです。 バリューには「単値」または「Javas …

 

shingo.nakanishi
 

東京在勤、1977年生まれ、IT職歴2n年、生涯技術者として楽しく生きることを目指しています。デスマに負けず健康第一。