One IT Thing

IT業界で飯を食う為の学習系雑記

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
-, , ,

執筆者:

関連記事

B2BスマホアプリをGooglePlay、AppStoreに公開することがお勧め出来ない7つの理由とその対策

商材の性質やシーンに応じてスマホアプリをGooglePlayやAppleStoreのようなストアに公開することがマイナスに働くこともあります。 商材として価値の有る電子データをお持ちの商社さんとアプリ …

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

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

ブラウザから起動したカメラの撮影画像をjavascriptで圧縮【Compressor.js】

「モバイル用Webアプリで撮影したカメラ画像のファイルサイズが大きすぎる・・・」 そんな悩みは無いですか? 昨今カメラ会社の経営が傾くほどスマホのカメラ性能が向上、それに応じて年々ファイルサイズも増大 …

キャッシュされているはずのServiceWorker資源にオフラインアクセス出来ない(Workbox + ionicons)

そのHTTPリクエストしたファイル資源、ひょっとしてURLパラメータついてたりしませんか? 目次1 事象2 原因3 対処 事象 ionic3(SPA)でWorkboxを使ったServiceWorker …

Angular、React、Vueプロジェクトを新規作成した時に生成されるリソース規模比較

アンチウィルスのディスクフルスキャンが日に日に遅くなっていく・・・ 一年後には丸一日掛かっても終わらなくなり、仕事終わりにかけて行ったフルスキャンが翌日まだ頑張っている。 この原因、ちょこちょこ作って …

 

shingo.7k24
 

東京在勤、職歴20年越え中年ITエンジニアです。まだ開発現場で頑張っています。

19歳(1996年)から書き始めた個人日記が5,000日を超え、残りの人生は発信をして行きたいと思い、令和元日からこのサイトを開始しました。勉強と試行錯誤をしながら、自分が経験したIT関連情報を投稿しています。

私と同じく、今後IT業界で生計を立てて行きたいと考えている方や、技術共有したいけどフリーランスで孤独、といった方と一緒に成長、知識共有して行けたら楽しいな、と思っています。