ionic async awaitでAPIへ同期的にリクエスト

やりたいことはタイトルのとおり。初めてでハマったのでコードをメモしておく。

import { Component } from '@angular/core';
import { Http, Headers,RequestOptions } from '@angular/http'

import 'rxjs/add/operator/map'
import 'rxjs/add/operator/catch'

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(
    public http: Http
  ) {
  }

  ionViewDidEnter() {
    this.requestApiAsync()
  }

  async requestApiAsync() {
    for (var i = 1; i <= 3; i++) {
      console.log(i + '回目')
      let result = await this.requestApi()
      console.log(result)
    }
  }

  requestApi() {
    return new Promise((resolve, reject) => {
      let url = 'http://example.com/path/to'
      let body = 'key=value'

      let headers = new Headers({
	"Content-Type": "application/x-www-form-urlencoded"
      })

      let options = new RequestOptions({
	headers: headers
      })

      this.http.post(url, body, options)
	.map(response => response).catch(error => error)
	.subscribe(
          result => resolve(result.json()),
          error => reject(error)
	)
    })
  }
}

以上です

ionic3 aws s3 へ画像や動画アップロード

ざっくり以下の流れで実装できた。
awsのアクセスキーとシークレットキーを入手
・s3のcorsの設定
aws-sdkjavascript版)を入手
・ionic 処理実装

アクセスキーとシークレットキーをコードに埋め込むのは現実的ではないが今回は動作確認という意味でその形としました。

s3 corsの設定
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
  <AllowedOrigin>*</AllowedOrigin>
  <AllowedMethod>PUT</AllowedMethod>
  <AllowedHeader>Authorization</AllowedHeader>
  <AllowedHeader>Content-Type</AllowedHeader>
  <AllowedHeader>X-Amz-Date</AllowedHeader>
  <AllowedHeader>X-Amz-User-Agent</AllowedHeader>
</CORSRule>
</CORSConfiguration>
aws-sdkの入手

と言っても今回はcdnを直接貼り付けただけ

src/index.html

<head>
・・・
  <script src="https://sdk.amazonaws.com/js/aws-sdk-2.2.37.min.js"></script>

</head>
実装

src/pages/home/home.ts

import { Component } from '@angular/core';

declare var AWS :any

・・・

export class HomePage {

  uploadFile = () => {

    let REGION = 'ap-northeast-1''
    let ACCESS_KEY = 'xxxxxxxxxxxxxx'
    let SECRET_KEY = 'xxxxxxxxxxxxxx'
    let BUCKET = 'test-bucket'

    let file = Fileオブジェクト
    let key = 'path/to/file.mp4'
    let contentType = 'video/mp4'

    let blobUrl = window.URL.createObjectURL(file)

    var xhr = new XMLHttpRequest();
    xhr.open('GET', blobUrl, true)
    xhr.responseType = 'blob'
    xhr.onload = function(e) {

    AWS.config.update({
      region: REGION,
      accessKeyId: ACCESS_KEY,
      secretAccessKey: SECRET_KEY
    })

    var bucket = new AWS.S3({ params: { Bucket: BUCKET } })
    bucket.putObject({
      Key: key,
      Body: xhr.response,
      ContentType: contentType
    },(error, data) => {
    })

  }
  xhr.send()
}

以上です

参考URL
https://dev.classmethod.jp/cloud/aws/s3-cors-upload/
http://yamano3201.hatenablog.jp/entry/2016/03/05/214018#S3%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AFKey%E3%81%A8Body%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B

javascript input file で選択した画像をBlobURLで表示

最近、やることが多いのでひな形をメモしておく

<input type="file" id="upload_file" />
<script>
window.addEventListener('load', function() {
  var upload_file = document.getElementById('upload_file');
  upload_file.addEventListener('change', function() {
    var blobUrl = window.URL.createObjectURL(upload_file.files[0]);
    // blobUrlをimgタグやvideoタグにセットする
  });
});
</script>

以上です

ionic3 localの動画をvideoタグで再生

はじめに

今回やりたかったことは以下
・フォトライブラリから選択した動画をvideoタグで再生
・カメラを起動して撮影した動画をvideoタグで再生

基本的な方針は以下に書いた画像の場合と同じ
http://kimagureneet.hatenablog.com/entry/2018/09/05/165323

1. ファイルへの絶対パスを取得。取得したパスに「file://」がついてなければ付ける
2. 1.で取得したURLを引数に、Ionic.WebView.convertFileSrcメソッド(cordova-plugin-ionic-webview)でローカルサーバーのURL(http://localhost:8080)に変換
3. 2.で取得したURLからBlob URLを作成
4. 3.で取得したBlob URLをvideoタグに設定

大きく違う点が3.で、localサーバーのURLからBlobURLを作成しているところ。Blob URLを使った理由は以下。
iOSで再生できなかった
Androidでは再生されるがvideo.currentTimeを設定しても期待した挙動にならなかった

それぞれ理由はわからないのだがブラウザの仕様なんじゃないかとちょっと気持ち悪いけど勝手に解釈。Androidのvideo.currentTimeが動かない点については同じコードでもローカルの動画の場合に期待通りに動かないということについてstack overflowでも触れられてる方がいた。

使用したプラグイン
https://github.com/apache/cordova-plugin-camera
https://github.com/apache/cordova-plugin-media-capture

実装

html

<button ion-button (click)="openPhoto()">フォトライブラリ</button>
<video controls muted="true" webkit-playsinline playsinline preload="auto" style="width:100%;" [src]="videoUrl"></video>

<button ion-button (click)="openCamera()">カメラを起動</button>
<video controls muted="true" webkit-playsinline playsinline preload="auto" style="width:100%;" [src]="videoCameraUrl"></video>

javascript(typescript)

import { Component } from '@angular/core'
import { Platform } from 'ionic-angular'
import { Camera } from '@ionic-native/camera'
import { PhotoLibrary } from '@ionic-native/photo-library'
import { MediaCapture } from '@ionic-native/media-capture';

declare var Ionic :any
declare var window;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  public imageUrl = ""
  public imageCameraUrl = ""

  constructor(
    public navCtrl: NavController,
    public platform: Platform,
    public camera: Camera,
    public mediaCapture: MediaCapture
  ) {
  }

  // フォトライブラリ
  openPhoto = () => {
    const options = {
      quality: 100,
      sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
      destinationType: this.camera.DestinationType.FILE_URI,
      mediaType: this.camera.MediaType.VIDEO
    }
    this.camera.getPicture(options).then((url) => {
      let videoPath = this.platform.is('android') ? 'file://' + url : url
      let videoUrl = Ionic.WebView.convertFileSrc(videoPath)
      this.createBlobUrl(videoUrl)
      .then(blobUrl => {
        this.presentVideoEditor(blobUrl, videoPath)
      })
      .catch(error => {
        console.log(error)
      })
    })
  }

  // カメラ
  openCamera = () => {
    this.mediaCapture.captureVideo().then(data => {
      let url = Ionic.WebView.convertFileSrc(data[0]['fullPath'])
      if (this.platform.is('ios')) {
        url = 'file://' + url
      }
      this.createBlobUrl(url)
      .then(blobUrl => {
	this.videoCameraUrl = blobUrl
      })
      .catch(error => {
	console.log(error)
      })
    }, error => {
      console.log(error)
    })
  }

  // Blob URL生成
  createBlobUrl = (url) => {
    return new Promise((resolve, reject) => {
        var xhr = new XMLHttpRequest()
        xhr.open("GET", url)
        xhr.responseType = "blob"
        xhr.addEventListener('load', () => {
          // BlobデータからBlob URLを生成
          let blob = xhr.response
          blob.name = 'file'
          blob.lastModifiedDate = new Date()
          let blobUrl = window.URL.createObjectURL(blob)
          resolve(blobUrl)
        })
        xhr.onerror = (error) => {
          reject(error)
        }
        xhr.send();
    })
  }
}

以上です

cordova plugin で not defined in Pluginエラー @iOS(swift)

swiftでcordovaプラグイン作ったら以下のエラー。
何度見直しても原因がわからずいたところ、swiftのバージョンをxcodeで直接いじっていたのが問題だった。

ERROR: Method 'my_method:' not defined in Plugin 'MyPlugin'
FAILED pluginJSON = ["MyPlugin1887867393","MyPlugin","my_method",[{"hoge":"fuga"}]]

https://www.npmjs.com/package/cordova-plugin-add-swift-support
こちらのプラグイン使うことになると思うがswiftのバージョンを見直した方がよいかも。以上です

ionic3 localの画像をimgタグに表示

はじめに

cordovaでのアプリ開発。webviewでimgタグにローカルの画像のパスを指定するだけと思ってたけど意外にはまったのでメモしておく。

今回やりたかったことは以下
・端末のフォトライブラリから選択した画像をimgタグに表示
・カメラを起動して撮影した画像をimgタグに表示
・ionic3で実装

使用したプラグインは以下
・cordova-plugin-camera : フォトライブラリへのアクセスとカメラの撮影に使用
・cordova-plugin-filepath : 後述するがAndroidでの画像ファイルへの絶対パスを取得するのに使用

方針

やり方調べていたら情報が実装方法が色々出てきたり古かったりと混乱した。「cdvfile://」「cdvphotolibrary://」とか専用のプロトコルみたいなのも色々出てきた。これらを試そうとしたら、ブラウザ側でプロトコルが不明やらContent Security Policy の設定が足りなかったりとエラーとなり結局やり方がわからなかった。ionic2以降ではcordova-plugin-ionic-webviewがデフォルトで組み込まれていてiOSではUIWebViewでなくWKWebViewがブラウザエンジンとなっていたりするらしい。

で、結局上手くいったやり方は
1. ファイルへの絶対パスを取得。取得したパスに「file://」がついてなければ付ける
2. 1.で取得したURLを引数に、Ionic.WebView.convertFileSrcメソッド(cordova-plugin-ionic-webview)でローカルサーバーのURL(http://localhost:8080)に変換
3. 2.で取得したURLをimgタグに設定

実装

html

<button ion-button (click)="openPhoto()">フォトライブラリ</button>
<img [src]="imageUrl" />

<button ion-button (click)="openCamera()">カメラを起動</button>
<img [src]="imageCameraUrl" />

js(typescript)

import { Component } from '@angular/core'
import { Platform } from 'ionic-angular'
import { Camera } from '@ionic-native/camera'
import { FilePath } from '@ionic-native/file-path'
import { PhotoLibrary } from '@ionic-native/photo-library'

declare var Ionic :any
declare var window;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  public imageUrl = ""
  public imageCameraUrl = ""

  constructor(
    public navCtrl: NavController,
    public platform: Platform,
    public filePath: FilePath,
    public camera: Camera,
    public photoLibrary: PhotoLibrary
  ) {
  }

  // フォトライブラリ
  openPhoto = () => {
    const options = {
      quality: 100,
      sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
      destinationType: this.camera.DestinationType.FILE_URI,
      mediaType: this.camera.MediaType.PICTURE
    }
    this.camera.getPicture(options).then((url) => {
      if (this.platform.is('ios')) {

        // url => file://path/to/img.jpg

        // imageUrl => http://localhost:8080/path/to/img.jpg
        let imageUrl = Ionic.WebView.convertFileSrc(url)

        this.imageUrl = imageUrl

      } else if (this.platform.is('android')) {
        // url => content://path/to/img.jpg

        this.filePath.resolveNativePath(url)
        .then(filePath => {

          // filePath => file://path/to/img.jpg

          // imageUrl => http://localhost:8080/path/to/img.jpg
          let imageUrl = Ionic.WebView.convertFileSrc(filePath)

          this.imageUrl = imageUrl
        })
      }
    })
  }

  // カメラ
  openCamera = () => {
    const options = {
      quality: 100,
      sourceType: this.camera.PictureSourceType.CAMERA,
      destinationType: this.camera.DestinationType.FILE_URI,
      mediaType: this.camera.MediaType.PICTURE
    }
    this.camera.getPicture(options).then((url) => {

      // url => file://path/to/img.jpg

      // imageUrl => http://localhost:8080/path/to/img.jpg
      let imageUrl = Ionic.WebView.convertFileSrc(url)

      this.imageCameraUrl = imageUrl
    })
  }

方針のところに書いたとおり、「file://」から「http://」に変換してimgタグにセット。うただしAndroidの場合は「content://」で返ってくるパターンもあるので、その場合は cordova-plugins-filepathプラグインのresolveNativePathを使って「file://」の形に変換することで対応できる。

以上です

Javascriptでcss(style)をまとめて一括セットする方法メモ

大した話じゃないけどいつも忘れるのでメモ

1つずつちまちまセットしてる

var div = document.createElement('div')
div.style.color = 'red'
div.style.fontSize = '18px'
div.style.fontWeight = 'bold'
・・・

まとめて一括で設定もできた

var div = document.createElement('div')
div.setAttribute('style', 'color:red; font-size:18px; font-weight: bold')

以上です