noxi雑記

.NET、Angularまわりの小ネタブログ

Angular CLIでJSZipを使用する

JavaScript で ZIP ファイルを動的に生成できる JSZip というライブラリがあります。とても便利なのですが Angular CLI プロジェクトで使用するとコンパイルエラーになるので、その解消方法のメモです。

前提条件

以下の環境で動作検証しています。

Angular CLI: 7.3.9
Node: 10.15.3
OS: win32 x64
Angular: 7.2.15
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.9
@angular-devkit/build-angular     0.13.9
@angular-devkit/build-optimizer   0.13.9
@angular-devkit/build-webpack     0.13.9
@angular-devkit/core              7.3.9
@angular-devkit/schematics        7.3.9
@angular/cli                      7.3.9
@ngtools/webpack                  7.3.9
@schematics/angular               7.3.9
@schematics/update                0.13.9
rxjs                              6.3.3
typescript                        3.2.4
webpack                           4.29.0
-----------------------------------------------------------
jszip                             3.2.1

プロジェクト準備

適当な Angular CLI のプロジェクトを作成し、 JSZip を追加でインストールします。 JSZip には TypeScript の型定義ファイルが含まれていないため、型定義ファイルも別途インストールが必要になります。

ng new angular-jszip-sample
cd angular-jszip-sample
npm i -P jszip @types/jszip

JSZip を使用した ZIP ファイルの作成(失敗する版)

※JSZip 自体の使い方についてここで特に触れることはしませんので、使い方を知りたい方はドキュメント等を参考にしてください。

JSZip を使用してファイルを生成するように AppComponent の中身を書き換えます。

// src/app/app.component.html

<button (click)="createZip()">Create ZIP file</button>
// src/app/app.component.ts

import { Component } from '@angular/core';
import * as JSZip from 'jszip';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  async createZip() {
    const zip = new JSZip();
    zip.file('sample.json', JSON.stringify({ hoge: 'fuga' }));

    // ZIPファイルのblobデータ生成
    const zipFile = await zip.generateAsync<'blob'>({
      type: 'blob',
      platform: 'UNIX',
      compression: 'DEFLATE',
      compressionOptions: { level: 9 },
    });

    // ダウンロード処理など
  }

}



そしてこれを実行すると落ちます。。。

ERROR in ./node_modules/jszip/lib/readable-stream-browser.js
Module not found: Error: Can't resolve 'stream' in 'C:\Users\noxi\Projects\angular-jszip-sample\node_modules\jszip\lib'
i 「wdm」: Failed to compile.

JSZip を使用した ZIP ファイルの作成(成功する版)

なにやら依存解決系で失敗している雰囲気があるので、端から綺麗に実行するのを諦めて JSZip を window 下に読み込んで使うことで解決できます。

// angular.json

...

"assets": [
  "src/favicon.ico",
  "src/assets"
],
"styles": [
  "src/styles.css"
],
"scripts": [
  "node_modules/jszip/dist/jszip.min.js"
],
"es5BrowserSupport": true

...
// src/app/app.component.ts

import { Component } from '@angular/core';
import * as JSZip from 'jszip';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  async createZip() {
    // windowから参照する様に変更した
    // 型定義自体は利用したいため左辺で指定する
    const zip: JSZip = new (window as any).JSZip();
    zip.file('sample.json', JSON.stringify({ hoge: 'fuga' }));

    // ZIPファイルのblobデータ生成
    const zipFile = await zip.generateAsync<'blob'>({
      type: 'blob',
      platform: 'UNIX',
      compression: 'DEFLATE',
      compressionOptions: { level: 9 },
    });

    // ダウンロード処理など
  }

}