[Angular JavaScript] JSONファイル(複数)の読み込み

  • Angular APにFile APIでローカル端末からファイルを読み込む機能を実装する手法を解説します
  • WEB APはセキュリティの関係で制約が多いので、やり方が限られます

1. 基礎知識

1.1. File API

  • File API

    • HTML5で定義された、ファイル操作のためのAPI
    • MDN説明
      1
      2
      File インターフェイスは、ファイルについての情報を提供したり、ウェブページ内の JavaScript でその内容にアクセスできるようにしたりします。
      File オブジェクトは一般的に <input> 要素を使用してユーザがファイルを選択した結果として返された FileList オブジェクトや、ドラッグアンドドロップ操作の DataTransfer オブジェクト、 HTMLCanvasElement の mozGetAsFile() API から情報を取得します。
    • 参考
  • できないこと

    • ディレクトリの読み込み
      • ファイル単位の読み込みしかできません
    • ユーザーの許可無しの読み込み
      • 自動的にローカルのファイル一覧をWEB AP上に表示するような仕様は実現できませんでした

1.2. Native File System API

2. 実装手順

  • 以下を構成を想定
    • sample.component.html
    • sample.component.ts

2.1. HTML Fileのinputを用意

htmlにinputボタンを用意します

  • sample.component.html

    1
    <input type = "file" [accept]="'.json'" (change)="selectFile($event)" #fl multiple id ='file'>
  • ファイルタイプを限定

    • acceptで定義
      1
      [accept]=""
  • 複数ファイルを取得

    • multiple
  • (change)でファイル読み込み時に実行するメソッドを定義

    • $eventに読み込んだファイルのデータが入ります
  • 読み込んだファイルを一覧表示

    • 読み込んだファイル名をfileNamesに格納してngForで表示する
      1
      2
      3
      4
      5
      <div>
      <ng-container *ngFor="let fileName of fileNames; let i = index" >
      <li> No. {{i+1}} | Name {{ fileName }} </li>
      </ng-container>
      </div>

2.2. Fileのデータの取得

  • ロジック

    • ファイル読み込み時に(change)イベントでメソッドが発火
    • 引数$eventとしてファイルのデータをメソッドに渡す
    • .target.filesでFileListオブジェクトを取得
    • ファイル数回のループを実行
      • ファイルを読み込むためにFileReaderオブジェクトを生成
      • .readAsText()でFileオブジェクトの読み込みを実行
        • .onloadで読み込み成功時の処理を定義
          • 読み込んだデータ(.result)を配列に格納
        • ..onerrorで読み込み失敗時の処理を定義
          • Errorメッセージを表示
  • sample.component.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    export class SampleComponent implements OnInit {

    // 選択したファイルの中身のデータを受け取る変数を宣言
    public filesData = [];

    // File読み込み時に発火するメソッド
    selectFile(evt) {

    // 変数データの初期化
    this.filesData = []; // ファイルの中身のデータ

    // inputしたファイルをFilelistオブジェクトとして取得
    // 複数のfileオブジェクトがselectfilesに入る
    let selectFiles = evt.target.files;

    // --------------fileを読み込む------------------------

    // ファイル数 = selectFiles.length回 ループさせる
    for (let i = 0, num = selectFiles.length; i < num; i++) {

    // 1. ファイルを読み込むためにFileReaderオブジェクトを生成
    // 1ファイルしか読み込めない為、ここもループが必要
    const rdr = new FileReader();

    // 2. ファイルをテキストとして読み込む
    rdr.readAsText(selectFiles[i]);

    // 3. 読み込みが成功した際のイベントを定義 loadイベントのハンドラー
    // TSの場合アロー関数でないと動かない
    rdr.onload = (e) => {
    // reader JSONファイルの中身のデータ

    // 4. 読み込んだデータを配列に格納
    this.filesData = this.filesData.concat(rdr.result);
    // console.log(this.filesData); // 配列にファイルの中身が追加されている

    }

    // ファイルの読みこみエラー時の処理 errorイベントのハンドラー
    rdr.onerror = (e) => {
    // console.log('ファイルを読み込めません');
    }
    }
    }

2.3. FileのMetaDataの取得

  • 仮定
    • ファイル名を取得して、選択ファイル一覧を表示したい
    • ファイル名はprefixに時刻データがついたものを想定
      1
      202051820143_XXXXXXX.json
  • sample.component.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    export class SampleComponent implements OnInit {

    // MeataDataを受け取る変数を宣言
    public time = []; // 取得データのタイムスタンプ
    public filesNames = []; // 選択したファイルの情報表示用に名前を格納する

    // 選択したファイルの中身のデータを受け取る変数を宣言
    public filesData = [];

    // File読み込み時に発火するメソッド
    selectFile(evt) {

    // 変数データの初期化
    this.time = []; // 取得データのタイムスタンプ
    this.filesName = []; // ファイル名
    this.filesData = []; // ファイルの中身のデータ

    // inputしたファイルをFilelistオブジェクトとして取得
    // FileListオブジェクトの中に複数のfileオブジェクトが含まれる
    let selectFiles = evt.target.files;

    // データ取得ロジック
    // -----略-------

    // MetaDataを取得するロジック
    for (let i = 0, numFiles = selectFiles.length; i < numFiles; i++) {

    // 1. FileListから単一のfileオブジェクトを抽出
    const f = selectFiles[i]; // fileオブジェクトを格納

    // 2. ファイル名から拡張子を除いた値を取得
    const filename = f.name.match(/(.*)\.json$/)[1];

    // 3. ファイル名を分解して要素情報を取得
    const tmp = filename.split('_');
    // console.log(tmp); // ["202051816143", "XXXXXXX"]

    // 4. 各要素の値を抽出

    // 4.1. タイムスタンプを配列として複数取得

    if (this.time !== tmp[0]) {
    this.time = this.time.concat(tmp[0]);
    }

    // 4.2. XXXXX部分
    this.filesName = this.selectedRegions.concat(tmp[1]);
    }
    console.log(this.filesName);
    // HTMLにfilesNameを双方向バインディング指定していれば、画面に表示される

参考

関連記事

MDNドキュメント

File APIを使用

jQuery or Pythonを使用

その他

JSON JavaScriptでの扱い

[Angular JavaScript] JSONデータのファイル化と出力 ~取得したデータを任意の名称で保存するロジック~ (TypeScript)

Angular APでローカル端末にファイルを出力する機能をWEB APに実装する手順を解説します。外部API(ex. AWS)からService経由でGETしたデータをLocal端末に保存するケースを想定しています。色々試したものの、WEB APはセキュリティ制約が多いので、やり方が限られました。

1. 基礎知識

1.1. 今回の手法(JacaScriptのdownloadを使用)

詳しくは手順を参考にしてください

  • リンクタグのDOMを取得してそこからdownload

    • 保存するJSONファイルの名前を一意に定義
    • レスポンスデータをJSON形式に変換
      • JSON.stringify()を使用
    • HTMLのリンク要素を生成
      • link = document.createElement()
    • リンク先にJSON形式の文字列データを置いておく
      • link.hrel = ‘data:text/plain,’ + encodeURIComponent(data);
    • 保存するJSONファイルの名前をリンクに設定
      • link.download = fileName
    • ファイルを保存
      • link.click()で自動で疑似的にリンクをクリックさせる
  • ポイント

    • プラグインの導入や、ブラウザの制約無しという条件であれば、これ以外の手法は現状ありませんでした
    • ファイルの保存先は指定不可
      • 端末の”ダウンロード”に入ります
    • セキュリティの都合上、ブラウザから直接端末のドライブに読み書きできないようになっているようです

1.2. その他の手法

  • Device Storage API
  • Native File System API
    • 保存先の指定が可能
      1
      このAPIはユーザーのデバイス上にあるファイルを読み込んだり、任意のディレクトリにファイルを書き込むことができるAPIです。途中段階のAPIですが、このAPIが提供されることでスマホアプリやデスクトップアプリのような機能をWebアプリでも提供できるようになります。
    • 今回は採用しません
      • ブラウザによっては動かず、APの利用に前提条件ができてしまう為
    • 採用する場合は以下を参考にどうぞ

2. 単一ファイルを取得

以下の構成を想定

  • get-file.component
    • get-file.component.html
    • get-file.component.ts
  • service
    • sample.service

2.1. get-file.component.html

  • (click)イベントでメソッドを起動するボタンを作成

    • TS側に外部からデータを取得して、ファイルとして保存するロジックを書きます
  • get-data.component.html

    1
    <button mat-raised-button (click)="submit()">Get Data</button>
  • その他:フォームやチェックリスト等を作成

    • 問い合わせ用のパラメータ設定をユーザが自由に弄れるようにするため
    • 別記事で解説 & 今回の趣旨と異なるため省略

2.2. get-file.component.ts

(click)イベントで発火するメソッドをこちらで作りこみます

  • submit()
    • 機能
      • 外部からJSONデータを取得
        • 外部APIへの問い合わせを実行するServiceを呼び出す
      • ファイル名を自動生成
        • 時刻を自動取得&フォームの入力値と合わせる
      • 取得データを加工してJSONファイル化

2.3. 実装例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
submit() {

// 1. 各種パラメータの取得
// ----1.1. ファイル名のprefixに付ける日時データを取得---
// Dateオブジェクトの作成
const now = new Date();
// 各日時要素を取得
const year = String(now.getFullYear()); // 年
// 1月=0と出るため+
const month = String(now.getMonth() + 1); // 月
const date = String(now.getDate()); // 日
const hour = String(now.getHours()); // 時
const min = String(now.getMinutes()); // 分
const sec = String(now.getSeconds()); // 秒
// YYYYMMDDHHMMSの形式で変数timeにまとめる
const time = year + month + date + hour + min + sec;

// 1.2. AWSへの問い合わせ用のパラメータを取得
// 今回はAWSから情報を取得するパターンを想定
// (認証とregionの指定、その他パラメータが必要)
// 1.2.1. Credential認証
AWS.config.credentials = new AWS.Credentials(AWS_CONFIG.accessKeyId, AWS_CONFIG.secretAccessKey);

// 1.2.2. チェックされたリージョンリストを取得
const REGION = this.makeRegionList();

// 1.2.3. 問い合わせ用のパラメータ(ex. sampleParameter)の取得
const parameter = this.makeSampleParameterList();

// 2. 外部APIへの問い合わせとファイル化----
// Service経由でデータを取得
// forkjoinを利用する為に返り値をobservablesに格納
let observables = REGION.map(region => { // 引数としてRegion情報を渡す
return this.sampleService.getData(region, resourceType).pipe(catchError(e => observableOf({"error": e})))
});

// 3. ファイル化
// リクエストの終了を待って返り値に処理を加える
// 非同期処理の待ち合わせの為にforkJoinを利用
forkJoin(observables).subscribe( response => {
// Observableをsubscribeして、中の値を取り出し、ファイルとして出力

// レスポンスを加工してjsonファイルとURLを作る

// 3.1. 保存するJSONファイルの名前: yyyymmddhhmmss-region_name.json とする
// 拡張子をfiletypeで指定
const filetype = '.json';
// file名を設定 
const fileName = time + '_' + REGION + filetype;

// 3.2. データをJSON形式の文字列に変換する。
const data = JSON.stringify(response);

// 出力:リンクタグのDOMを取得してそこから行う

// 3.3. HTMLのリンク要素を生成する
const link = document.createElement('a');

// 3.4. リンク先にJSON形式の文字列データを置いておく。
link.href = 'data:text/plain,' + encodeURIComponent(data);

// 3.5. 保存するJSONファイルの名前をリンクに設定
link.download = fileName;

// 3.6. ファイルを保存
link.click();

});

3. 複数ファイルをダウンロード

処理を複数回実行する必要があります。問い合わせ処理とファイル化処理を特定のパラメータに応じてfor文でループさせます。

  • 想定するシチュエーション
    • AWSからデータを取得
      • 指定した各region毎の情報をファイル化します
  • 機能連携イメージ
    • ボタンを押下
      • submit()メソッドが発火
    • 各種パラメータを取得
      • ファイルの命名と問い合わせに必要な情報を集める
    • 問い合わせを実行(データを持ってくる)
      • 特定のパラメータ数回、外部からデータを取得 ⇒ 配列に格納
        • 外部APIに問い合わせを実行するサービス(sampleService)を呼び出して処理はそちらに任せる
    • ファイル化処理
      • ファイルを分けたい単位(特定のパラメータより規定)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// データを取得 ⇒ ローカルにJSONファイルを保存するメソッド
submit() {

// 1. 各種パラメータの取得
// ----1.1. ファイル名のprefixに付ける日時データを取得---
// Dateオブジェクトの作成
const now = new Date();
// 各日時要素を取得
const year = String(now.getFullYear()); // 年
// 1月=0と出るため+
const month = String(now.getMonth() + 1); // 月
const date = String(now.getDate()); // 日
const hour = String(now.getHours()); // 時
const min = String(now.getMinutes()); // 分
const sec = String(now.getSeconds()); // 秒
// YYYYMMDDHHMMSの形式で変数timeにまとめる
const time = year + month + date + hour + min + sec;

// 1.2. AWSへの問い合わせ用のパラメータを取得
// 今回はAWSから情報を取得するパターンを想定
// (認証とregionの指定、その他パラメータが必要)
// 1.2.1. Credential認証
AWS.config.credentials = new AWS.Credentials(AWS_CONFIG.accessKeyId, AWS_CONFIG.secretAccessKey);

// 1.2.2. チェックされたリージョンリストを取得
const REGIONS = this.makeRegionList();

// 1.2.3. 問い合わせ用のパラメータ(ex. sampleParameter)の取得
const parameter = this.makeSampleParameterList();

// 2. 外部APIへの問い合わせとファイル化----
// 以降の処理をsampleParameter毎にループさせる

for (let a = 0, numSampleParameter = sampleParameter.length ; a < numSampleParameter; a++) {

// ------2.1. region数回 AWSへの問い合わせを実行---------
// forkjoinを利用する為に返り値をobservablesに格納

const observables = REGIONS.map(region => { // 引数としてRegionを渡す
return this.sampleService.getData(region).pipe(catchError(e => observableOf({"error": e})))
});

// ------2.2. ファイル化処理をregion数回ループさせる-------

for (let i = 0, numRegions = REGIONS.length ; i < numRegions; i++) {
// リクエスト(非同期処理)の終了をforkjoinで待ち合わせる
forkJoin(observables[i]).subscribe( response => {
// Observableをsubscribeして、中の値を取り出し、変数データの内容を変数responseとして扱う

// 2.2.1. レスポンスを加工してjsonファイルとURLを作る
// 保存するJSONファイルの名前: yyyymmddhhmmss-region_name.json とする

// 拡張子をfiletypeに指定
const filetype = '.json';
// file名を設定 
const fileName = time + '_' + REGIONS[i] + filetype;

// 2.2.2. データをJSON形式の文字列に変換
const data = JSON.stringify(response);

// 出力:リンクタグのDOMを取得してそこから行う
// 2.2.3. HTMLのリンク要素を生成する。
const link = document.createElement('a');

// 2.2.4. リンク先にJSON形式の文字列データを置いておく
link.href = 'data:text/plain,' + encodeURIComponent(data);

// 2.2.5. 保存するJSONファイルの名前をリンクに設定
link.download = fileName;

// 2.2.6. ファイルを保存
link.click();
});
} // -------ファイル生成ループ終了--------
}
  • service側の処理や受け渡す変数、ファイル名はシチュエーションに合わせて補完してください

4. 参考

関連記事

ファイルのダウンロード

その他

jQuery or Pythonを使用

JSON JavaScriptでの扱い

MDNドキュメント

@EventEmitter @Input @Output @ViewChild ACM AMP API Gateway AR AR.js AR.js Studio AWS AWS Amplify AWS Budgets AWS Cost Explorer AWS SDK for JavaScript AddThis Adobe XD Alexa Amazon CloudWatch Amazon Honycode Amazon SNS Android Angular Angular Material AngularFire AppSync Augury C CDN CI/CD Cloud Craft Cloud9 CloudFormation CloudFront CloudTrail Cognito Cost Anomaly Detection Cubase ai Cubasis LE DTM Disqus DynamoDB Elgato HD 60S Firebase Firebase Hosting Former2 Github Github Actions Github.com GithubEnterprise GithubPages Google Chrome Google Cloud Shell GraphQL Hexo Hosting IAM Ionic JSON JavaScript LadioCast Logging LowCode MFA MS Authentication MacBook Pro 16 Mind Node NETDUETTO Netflix Party Netlify Network NoCode Observable PO PdM Promise RPA ReactiveForm Recognition Route53 S3 SAM(Serverless Application Model) SAR SSL SYNCROOM Schematics ScrumInc Serverless Service Siri Sitemap Spark AR Steinberg UR44C Teams Touch Cast Studio Treetable TypeScript UI UI Bakery WAF WAFv2 WEB Page Dev WEB会議 WebAR WebSocketAPI Webhook Windows Power Automate Wireshark aot async/await aws config cloud9 display.land draw.io e2e test filter() forkJoin() google search console hexo-generator-amp iOS iPad Pro iPhone icarus map() mat input mat tree mat-checkbox mat-input mat-selection-list mmhmm ngFor plantUML popIn Aladdin2 then() vscode ”global is not defined” らくがきAR アジャイル アジャイル開発 クロスプラットフォーム ショートカット スクラム スクラム開発 テレワーク ファイル操作 ブラウザ型IDE プロダクトオーナー プロダクトマネージャー プロトタイピング リモートセッション 共同開発 双方向データバインディング 待ち合わせ処理 認定スクラムマスター 静的WEB Hosting 静的WEBサイトHosting
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×