はじめに
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を外部ビューワアプリに頼らずに表示する為、後編に続きます。