Corredor

ウェブ、プログラミングの勉強メモ。

VSCode で Spring Boot アプリケーションの開発を始めてみる

僕の Java 経験というと、長らく Struts1 を業務で使わされてきていて、Struts2 や Spring などはコードをチラ見してデバッグしたりすることはあったものの、イチから作ってみたことがなかった。

そこで今回、Spring ベースのアプリケーションを簡単に構築できる Spring Boot を触ってみようと思い立った。さらに今回は、Java 開発でお馴染みの Eclipse ではなく、Node.js 開発で使い慣れた VSCode を IDE として使ってみようと思い、そのための設定も調べてみた。

ソースコードと参考文献

今回行う手順は、以下の記事を参考にしたモノである。

また、作成したソースコードは以下の GitHub リポジトリで公開しているので、コミット履歴ともども追ってみてほしい。

github.com

前提環境確認

まずは前提環境を確認する。今回は MacOS Mojave にて、JDK と VSCode をテキトーにインストールしてある状態を前提とする。

# MacOS 専用のこんなコマンドがあるのね…
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.5
BuildVersion:   18F132

$ java -version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

$ code -version
1.37.0
036a6b1d3ac84e5ca96a17a44e63a87971f8fcc8
x64

この JDK1.8 はどうやって入れたんだっけなぁ、忘れた。多分 Oracle のサイトからテキトーにダウンロードしてインストールしただけだと思う。JDK に関する環境変数は ~/.bash_profile にてこんな感じで指定してある。

export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home"
export _JAVA_OPTIONS='-Dfile.encoding=UTF-8'
export PATH="${JAVA_HOME}/bin:${PATH}"

VSCode 拡張機能を準備する

まずは VSCode で Java 開発、および Spring Boot 開発を行うために、拡張機能パックをインストールする。バージョンは本稿執筆時点のモノなので、特に気にせず最新版をインストールすれば良いかと。

  • Java Extension Pack (v0.7.1)
    • f:id:neos21:20190818145454p:plain
  • Spring Boot Extension Pack (v0.0.8)
    • f:id:neos21:20190818145504p:plain

プロジェクトを作成する

それではプロジェクトを作成していく。Cmd + Shift + P でコマンドパレットを開き、Spring と入力すると、

  • Spring Initializr: Generate a Maven Project

という項目が見えるはずなので、コレを選択する。

f:id:neos21:20190818145513p:plain

Specify project language.Java を選択する。

f:id:neos21:20190818145523p:plain

Input Group Id for your project. でパッケージ名 (Group ID) を入力する。ドメインの逆順で入力するいつものアレ。

f:id:neos21:20190818145527p:plain

Input Artifact Id for your project. でプロジェクト名 (Artifact ID) を入力する。demo という単語だけだとダメっぽい?ので、mydemo とか適当な名前にした。ハイフンケースも使えるが、Maven プロジェクトで問題が起こることもあるっぽい。この辺ベストプラクティスがよく分からん。

f:id:neos21:20190818145532p:plain

Specify Spring Boot version. で Spring Boot のバージョンを選択する。今回は 2.1.7 を選んだ。

f:id:neos21:20190818145536p:plain

Search for dependenies. で、依存関係を選択する。今回は2つほど選択する。

f:id:neos21:20190818145541p:plain

1つ目は Spring Web Starter。一般的なウェブアプリを作るための一式。

f:id:neos21:20190818145546p:plain

選択すると、Selected 1 dependency と表示され、Spring Web Starter にチェックが付く。

f:id:neos21:20190818145551p:plain

2つ目は Thymeleaf。テンプレート HTML を作るためのモノ。

f:id:neos21:20190818145556p:plain

選択すると Thymeleaf にもチェックが付くので、このまま Enter を押下する。

f:id:neos21:20190818145600p:plain

保存先ディレクトリを選択する。

f:id:neos21:20190818145610p:plain

コレでボイラープレートコードが出力された。

f:id:neos21:20190818145626p:plain

出力されたファイル群はこのとおり。target/ ディレクトリはコンパイルされた .class ファイルが吐かれる場所なので気にしなくて OK。

f:id:neos21:20190818145631p:plain

エントリポイントは

  • ./src/main/java/【パッケージ名】/【プロジェクト名】/DemoApplication.java

というファイル。このファイルだけだと、サーバが起動するだけで、何のページも表示されない。

f:id:neos21:20190818145640p:plain

サンプルページを作ってみる

前述の記事に沿って、サンプルページを作ってみる。

  • ./src/main/java/【パッケージ名】/【プロジェクト名】/

の下に controller/ ディレクトリを作り、その下に SampleController.java ファイルを作る。

  • ./src/main/java/【パッケージ名】/【プロジェクト名】/controller/SampleController.java
package 【パッケージ名】.【プロジェクト名】.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class SampleController {
  // URL を指定する
  @RequestMapping("/sample")
  public String sample() {
    // テンプレート名を指定する
    return "sample";
  }
}

f:id:neos21:20190818145652p:plain

続いて、

  • ./src/main/resources/templates/

配下に sample.html ファイルを作る。

  • ./src/main/resources/templates/sample.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Sample</title>
  </head>
  <body>
    <h1>Hello World</h1>
    <p>こんにちは世界</p>
  </body>
</html>

f:id:neos21:20190818145702p:plain

デバッグ開始

ファイルを作ったので、デバッグを開始して動きを見てみる。

VSCode の左端から、虫アイコン「デバッグ」を選択する。「デバッグ」欄は最初、「構成がありません」と表示されていると思うが、構わず再生アイコン「デバッグの開始」を選択する。すると「環境の選択」というダイアログが表示されるので、「Java」を選択する。

f:id:neos21:20190818145709p:plain

コレで ./.vscode/launch.json ファイルが自動生成される。

f:id:neos21:20190818145718p:plain

再度「デバッグ」ペインに戻り、

  • Debug (Launch)-DemoApplication

を選択して「デバッグの開始」とする。

f:id:neos21:20190818145725p:plain

Build failed, do you want to continue? のエラーが出る場合は…

ココで自分は、Build failed, do you want to continue? というエラーが出てしまい、上手くサーバを起動できなかった。

f:id:neos21:20190818145731p:plain

調べてみると、以下に答えがあった。

  • F1 キー押す
  • Java: Open Java Language Server log file を選択
  • 6行目に

Command-line arguments: -data C:\Users\xxxx\AppData\Roaming\Code\User\workspaceStorage\56838f4e76654cb1f380037ac07c0b54\redhat.java\jdt_ws

ってあるので、56838f4e76654cb1f380037ac07c0b54 のフォルダを削除するかリネームする。(VS Code 一旦終了してする)

Java Language Server が workspace を持ってるみたい。

このとおりやってみる。

まずは F1 キーを押下し、コマンドパレットから

  • Java: Open Java language server log file

を選択する。

f:id:neos21:20190818145736p:plain

すると .log というファイルが開かれるので、その6行目にあるはずの Command-line arguments: 行に書かれたディレクトリパスを控えておく。

f:id:neos21:20190818145741p:plain

MacOS の場合は

  • /Users/【ユーザ名】/Library/Application Support/Code/User/workspaceStorage/

配下に、ランダムな英数字のディレクトリができているようだ。

そこでこのディレクトリを削除する。以下のスクリーンショットでは VSCode 内のターミナルから消しているが、実際は VSCode を完全に閉じてから削除するべき。

f:id:neos21:20190818145747p:plain

ディレクトリを削除したら VSCode を再起動し、再度「デバッグの開始」を押下する。

1回やっただけだと上手く行かなかったりするが、2回繰り返したりすると解消したりした。

デバッグ開始後…

つまづいたところもあったが、ようやくデバッグサーバが起動できた。

f:id:neos21:20190818145757p:plain

「デバッグコンソール」が上のように出力されたら問題ないはずだ。ブラウザで http://localhost:8080/sample にアクセスして、自分で実装した SampleController.java を通って sample.html が表示されていることを確認する。

f:id:neos21:20190818145814p:plain

デバッグサーバを停止する場合は、VSCode の上部中央に表示されているコントローラ群から、赤い四角 のアイコンを押下すれば止められる。

以上

VSCode で Java 開発するための拡張機能の導入は簡単だし、Eclipse のようなモッサリ感がなくて嬉しい。独特なビルドエラーにつまづいたものの、案外サクサクと環境構築できた。

まだ Spring Boot の学習基盤を作った程度ではあるが、実装がシンプルで、「とりあえず思ったモノを実現する」にはもってこいだと感じた。もう少し凝ったアプリも作れるように学習していこう。

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発

現場至上主義 Spring Boot2 徹底活用

現場至上主義 Spring Boot2 徹底活用

Spring Boot 2 プログラミング入門

Spring Boot 2 プログラミング入門

改めて Windows Subsystem For Linux (WSL) を使ってみる

2017年4月、当時は「Bash on Ubuntu on Windows」という名前だったが、Windows 上で Linux を動かす方法を検証した。

neos21.hatenablog.com

あれから2年、まずサービス名称が変わり、今では「Windows Subsystem For Linux (WSL)」と呼ばれている。当時は Insider Program に参加してプレビュー版をインストールしないと使えなかったが、現在は Microsoft Store から簡単に導入できるようになっている。

で、現在一般的に導入できるのは WSL 1 というバージョンで、本稿執筆時点では Windows 10 Pro の Insider Preview Build 18917 以降で WSL 2 が使える状態だ。

WSL2 にも興味はあるのだが、まだプレビュー版なので、今回は Insider Program などが必要なく導入できる、WSL1 の導入方法を改めて紹介する。

WSL の導入方法

  1. Windows のバージョン確認。PC 設定 → システム → バージョン情報 → 「Windows の仕様」欄 → バージョン を確認。自分は Windows 10 Home の 1903 というバージョンで以降の作業を行った
    • f:id:neos21:20190807231856p:plain
  2. コントロールパネル → プログラムと機能 → Windows の機能の有効化または無効化 → 「Windows Subsystem for Linux」と「Windows ハイパーバイザープラットフォーム」にチェックを入れて再起動する。最初からチェック状態だった人は WSL が使える状態が既に出来ている
    • f:id:neos21:20190807231744p:plain
  3. Microsoft Store にて「Ubuntu」と検索。以下のように Ubuntu をインストールする
    • f:id:neos21:20190807231750p:plain
  4. インストールが完了すると、スタートメニュー → アプリ → Ubuntu というメニューが追加されているので、コレを押下して起動する。初回は以下のように Installing, this may take a few minutes... と表示されるので待つ
    • f:id:neos21:20190807231754p:plain
  5. 少し待つと、ユーザ名とパスワードを設定することになる。ユーザ名は大文字を入れられず小文字のみなので注意。入力が終わると初期設定が完了し、プロンプトが表示される
    • f:id:neos21:20190807231759p:plain

以上。以降はインストールした「Ubuntu」ショートカットから WSL を起動すれば良い。

Ubuntu 環境のアップデート・日本語化

WSL が起動できたら、最初に以下のコマンドを叩いて Ubuntu 環境を最新版にアップデートしておこう。

# パッケージリストの更新
$ sudo apt update
# パッケージのアップデート
$ sudo apt upgrade -y
# 上のコマンドで保留されたパッケージのアップデート
$ sudo apt dist-upgrade -y
# 不要になったパッケージの削除
$ sudo apt autoremove -y

また、日本語化は以下の要領で可能。

$ sudo apt install -y language-pack-ja
$ sudo update-locale LANG=ja_JP.UTF8

WSL のバージョン確認方法

本稿執筆時点で一般的にインストールできるのは WSL1 系となる。

WSL2 系では、管理者権限で PowerShell を開き、以下のコマンドを実行することで、WSL のバージョン確認ができるようだ。

> wsl -l -v

# もしくは
> wsl --list --verbose

また、WSL2 をインストールしたあとは、WSL1 環境の Linux を WSL2 にアップデートする時、--set-version というオプションも使えたりする。

-v--verbose--set-version というオプションは、WSL1 系では用意されていないので、実行してみると コマンド ライン オプションが無効です: というエラーが出て、コマンドのヘルプが表示されると思う。このような表示になったら、まだ WSL2 がインストールできていなくて、WSL1 が使われているということ。

ついでに:Windows Terminal のプレビュー版がインストールできた

そういえば、Microsoft Store アプリを見ていて気付いたのだが、Windows Terminal というターミナルアプリのプレビュー版が一般でもインストールできるようになっていた。Windows 10 は最新版にアップデートする必要があるようだ。

f:id:neos21:20190807231900p:plain

起動してみるとこんな感じ。フォントにアンチエイリアスがちょっとかかってて見やすいかも。

f:id:neos21:20190807231905p:plain

Windows コマンドプロンプトも開けるし、Azure にも接続できるみたい。

f:id:neos21:20190807231911p:plain

だがコレ以外に GitBash などを立ち上げるような、ConEmu みたいな設定はできなさそうだ。今後に期待。

参考

Oracle Object Storage REST API に PUT する時はリクエストヘッダを一部省略できた

以前、Oracle Object Storage の REST API を操作する Node.js スクリプトについて、公式のサンプルコードを日本語圏向けに修正した。

neos21.hatenablog.com

日本語を含むテキストファイルの末尾が欠落する問題は、上の記事のとおり Content-Length の指定を修正することで対処でき、しばらくは無事に使っていた。

しかし最近、サイズが大きいファイルを PUT 送信する時に、どうも送信前の処理に時間がかかっていることを見つけた。今回はコレを解決する。

Object Storage REST API のおさらい

Oracle Object Storage の REST API リファレンスは以下にある。

この中でよく使うと思われるのは、Object (≒ ファイル) のダウンロードやアップロードだろう。

今回、処理に時間がかかっていると分かったのは、この PutObject をコールしようとした時だった。

さて、Node.js 向けの公式サンプルコードは以下にある。Version 1.0.1 と打たれているコードだ。

この中の

request.setHeader("Content-Length", options.body.length);

部分を

request.setHeader('Content-Length', Buffer.byteLength(options.body, 'utf8'));

このように直すことで、日本語を含むファイルの末尾欠落を回避したのが、前回の記事だった。

処理に時間がかかっている場所

さて、PutObject を叩く時に処理に時間がかかっている場所はどこなのか。

コードの各行に console.log() を仕込むという、昔ながらの「プリント・デバッグ」で調べたところ、以下の1行が実行されるのに、2・3秒かかっていることが分かった。

shaObj.update(options.body);

コレが何なのか示すために、もう少し周辺のコードを抜粋する。

var methodsThatRequireExtraHeaders = ["POST", "PUT"];
if(methodsThatRequireExtraHeaders.indexOf(request.method.toUpperCase()) !== -1) {
  options.body = options.body || "";
  
  var shaObj = new jsSHA("SHA-256", "TEXT");
  shaObj.update(options.body);  // ← この行の処理に時間がかかってる
  
  request.setHeader("Content-Length", options.body.length);  // ← Buffer.byteLength(options.body, 'utf8') に変更すべき箇所
  request.setHeader("x-content-sha256", shaObj.getHash('B64'));
  
  headersToSign = headersToSign.concat([
    "content-type",
    "content-length",
    "x-content-sha256"
  ]);
}

このコードは、Oracle Cloud の REST API の内、POST と PUT リクエストを叩く時に必要となるリクエストヘッダを設定している。Content-Type の他、前回の記事で修正した Content-Length と、リクエスト情報のハッシュ値を X-Content-SHA256 というヘッダ名で付与している。

問題となっている shaObj.update() メソッドは、jssha という npm パッケージが提供するモノだ。直前の行で、jssha のインスタンスを生成していることが分かるだろう。後の行で shaObj.getHash('B64') とハッシュ値を取得するための準備として jssha.update() を叩くのだが、コレに時間がかかっていた。

ハッシュ値を作るには、一旦コンテンツの全量を読み込んで処理する必要がある。10MB とか 20MB とか、サイズの大きいファイルを送ろうとすると、それだけのテキストを jssha に渡して処理させることになる。この処理は同期的に行われ、CPU を使うために、シングルスレッドで動作する Node.js においてはブロッキングが発生する。ちなみに、CPU にコストのかかる処理のことは「CPU バウンドな処理」などと表現したりもする。

シングルスレッドであるために、CPU バウンドな処理でブロッキングが発生する現象は、大規模な JSON データを JSON.parse() したり JSON.stringify() したりする時にも発生する。今回はまさにコレと同じ原因だったのだ。

CPU バウンドな処理を別プロセスに分ける…?

さて、原因の箇所が分かったので、なんとかしたい。

Object Storage への PUT 送信以外にも、並列処理したいことがいくつかある。それなのに jssha.update() の実行に CPU を使われてしまうと、他の処理が非同期であっても、その間は処理がせき止められてしまう。

コレは Node.js がシングルプロセス、シングルスレッドで動作するためにブロッキングが発生する、と話した。それでは、この重たい処理だけ、別プロセスで実行させたら、スレッドを分割できるのではないか。

コレは確かに有効な策だった。PUT 送信をコールする部分から child_process.fork() で別プロセスに切り出して処理させたところ、別プロセスで jssha.update() に時間がかかっている最中でも、親プロセス側の並列処理はキレイに動いてくれた。ブロッキングは回避できたのである。

どのファイルを PUT 送信するか、というパラメータは ChildProcess.send() で送れるし、送信完了の通知が欲しければ、親プロセスで ChildProcess.on('message') イベントを予約しておき、子プロセスから process.send() を送信すれば受け取れる。

ということで、ブロッキングを回避するのはコレでも良かった。

そもそもそのリクエストヘッダが要らなかった

改めて Oracle Cloud の REST API に関するリファレンスを見ていると、驚愕の事実が発覚した。

必須ヘッダー

GET および DELETE リクエスト (リクエスト本文にコンテンツがない場合) の場合、署名文字列には少なくとも次のヘッダーが含まれている必要があります:

  • (request-target) (draft-cavage-http-signatures-08 で説明されているように)
  • host
  • date または x-date (両方が含まれている場合、Oracle は x-date を使用します)

ココまでは良い。GET の時は jssha を使った if 文のブロックに入らないが、上の3つのリクエストヘッダはその手前で設定されている。

PUT リクエストと POST リクエスト (リクエスト本文にコンテンツがある場合) の場合、署名文字列には少なくとも次のヘッダーが含まれている必要があります:

  • (request-target)
  • host
  • date または x-date (両方が含まれている場合、Oracle は x-date を使用します)
  • x-content-sha256 (オブジェクト・ストレージ PUT リクエストを除く、次のセクションを参照)
  • content-type
  • content-length

  • 警告

    • PUT および POST リクエストの場合、クライアントは x-content-sha256 をコンピュートし、本文が空の文字列であってもリクエストおよび署名文字列にそれを組み込む必要があります。
      また、本文が空であっても、リクエストと署名の文字列には常に content-length が必要です。
      一部の HTTP クライアントは、本文が空の場合は content-length を送信しないため、クライアントが明示的に送信するようにする必要があります。
      datex-date の両方が含まれている場合、Oracle は x-date を使用します。x-date は、リクエストの署名部分の再利用 (リプレイ攻撃) から保護するために使用されます。
  • 1つの例外は、オブジェクトに対するオブジェクト・ストレージ PUT リクエストです (次のセクションを参照)。

途中までそのとおり〜これらのヘッダが要るよね〜と思って読んでいたのだが、x-content-sha256 のところに「オブジェクト・ストレージ PUT リクエストを除く」と書いてあったのだ。

どういうことかと思い、続きを読んでみる。

オブジェクト・ストレージ PUT の特別な手順

オブジェクト・ストレージ PutObject と UploadPart PUT リクエストの場合、署名文字列には少なくとも次のヘッダーが含まれている必要があります:

  • (request-target)
  • host
  • date または x-date (両方が含まれている場合、Oracle は x-date を使用します)

リクエストにも PUT リクエスト (通常は上記のリストを参照) に必要な他のヘッダーも含まれている場合は、これらのヘッダーも署名文字列に含める必要があります。

通常 Oracle Cloud の REST API の内、POST や PUT リクエストを叩く時は、x-content-sha256content-length の指定が必須だが、Object Storage への PUT 送信のみはコレが不要だということが分かった。

公式のサンプルコードは、Object Storage に限らず他の Oracle Cloud REST API にも汎用的に対応するため、POST と PUT のリクエスト時は必ずこれらのヘッダを付けるように実装されていた。しかし、Object Storage PUT の場合だけは、この計算コストを省略できるワケだ。

自分はこのコードを Object Storage API にしか使っていなかったので、jssha を使う箇所をガッツリ削除することで、CPU バウンドな処理を削って処理を高速化できた。

// 削除したコードをコメントアウトで表現

var methodsThatRequireExtraHeaders = ["POST", "PUT"];
if(methodsThatRequireExtraHeaders.indexOf(request.method.toUpperCase()) !== -1) {
  options.body = options.body || "";
  
  // var shaObj = new jsSHA("SHA-256", "TEXT");
  // shaObj.update(options.body);
  
  // request.setHeader("Content-Length", options.body.length);
  // request.setHeader("x-content-sha256", shaObj.getHash('B64'));
  
  headersToSign = headersToSign.concat([
    "content-type",
    // "content-length",
    // "x-content-sha256"
  ]);
}

headersToSign に追加するヘッダ名もちゃんと削っておく。それ以外の細部も色々と最適化できそうではあるが、今回の問題点と効果を明らかにするため、本当に不要なところを削るだけに留めた。

x-content-sha256 のために使っていた jssha は使用箇所がなくなったので、依存する npm パッケージからも削れた。

Content-Length に関しては、Buffer.byteLength() で計算した値を指定せずに送信して、文字列の欠落は発生しないのか?と心配になったが、Content-Length ヘッダが未指定でもテキストファイルの欠落は発生しないようだった。一安心。ちなみにこの Buffer.byteLength() も、若干の計算コストが掛かっていたところだったので、コレも削れるとさらに処理速度が速まる。

あまりないとは思うが、Object Storage 以外の REST API もコールする場合は、上述のようにコードを削るだけではダメだ。今度はそれらの API をコールする際に x-content-sha256 ヘッダが不足してしまいエラーになるからだ。そのため、何らかの方法でコールする API を見極めて、PutObject の時だけヘッダ付与を回避してやらないといけないだろう。公式のコードでいくと、function sign() に Boolean 型の第3引数を追加してハンドリングするのがてっとり早いだろうか。

// 第3引数に isPutObject を追加
function sign(request, options, isPutObject) {
  var apiKeyId = options.tenancyId + "/" + options.userId + "/" + options.keyFingerprint;
  var headersToSign = [
    "host",
    "date",
    "(request-target)"
  ];
  
  var methodsThatRequireExtraHeaders = ["POST", "PUT"];
  if(methodsThatRequireExtraHeaders.indexOf(request.method.toUpperCase()) !== -1) {
    options.body = options.body || "";
    headersToSign = headersToSign.concat([
      "content-type"
    ]);
    
    // PutObject 以外のコールの場合は以下のヘッダも付与する (= PutObject の場合は以下のヘッダを付けない)
    if(!isPutObject) {
      var shaObj = new jsSHA("SHA-256", "TEXT");
      shaObj.update(options.body);
      request.setHeader("Content-Length", options.body.length);
      request.setHeader("x-content-sha256", shaObj.getHash('B64'));
      headersToSign = headersToSign.concat([
        "content-length",
        "x-content-sha256"
      ]);
    }
  }
  
  httpSignature.sign(request, {
    key: options.privateKey,
    keyId: apiKeyId,
    headers: headersToSign
  });
  
  var newAuthHeaderValue = request.getHeader("Authorization").replace("Signature ", "Signature version=\"1\",");
  request.setHeader("Authorization", newAuthHeaderValue);
}

…こんな感じでハンドリングすれば良さそう。

以上

公式のサンプルコードそのままでもとりあえずは動いたが、ドキュメントをよく見ると無駄があることが分かった。パフォーマンスを向上するには、無駄は極力省く必要がある。

今回はボトルネックになっている箇所が x-content-sha256 リクエストヘッダの生成処理部分だと分かり、ドキュメントで仕様を確認すると、このリクエストヘッダが不要だと分かった。そこで x-content-sha256content-length を削り、パフォーマンスを向上できた。

安易にサンプルコードをパクったつもりはなかったが、それが本当に必要な処理なのかどうかは、ホントに1行ずつ検証していかないといけないな、と思った。

Amazon Web Services パターン別構築・運用ガイド 改訂第2版 (Informatics&IDEA)

Amazon Web Services パターン別構築・運用ガイド 改訂第2版 (Informatics&IDEA)

iPhone 11 Pro Max・iOS 13 の写真の「調整を自動適用」がよく分からない

超広角レンズが搭載された iPhone 11 以降で、「設定」アプリ → 「カメラ」と移動し、「構図 (COMPOSITION)」欄を見ると、

  • 写真のフレームの外側を含めて撮影
    • Photos Capture Outside the Frame
  • ビデオのフレームの外側を含めて撮影
    • Videos Capture Outside the Frame

という設定がある。これらをオンにすると、

  • 調整を自動適用
    • Auto Apply Adjustments

という項目が登場する。コレについて、設定画面のすぐ下には以下のような説明がある。

写真またはビデオのフレームの外側の領域を撮影することで構図を改善することができます。フレームの外側の領域が補正に使用されなかった場合は30日後に削除されます。

Capture the area outside the frame of the photo or video to improve composition. If the area around the frame is not used to make corrections, it will be deleted after 30 days.

以下の記事でも「調整を自動適用」については少し触れたのだが、効果が現れるタイミングがよく分からないうえに、最近また違う表記を見つけたので、まとめてみる。

カメラロールで写真の右上に青色の「自動」バッジが表示されるパターン

恐らく、この「調整を自動適用」が有効になった写真は、このパターンのことなのかなと思っている。

カメラロールで撮影した写真を見ていると、写真の右上に青色の「自動」バッジが表示されることがある。コレを押下すると、超広角レンズで撮影したデータを合成して、うまく構図を調整してくれる。

f:id:neos21:20191026232909p:plain
↑右上の青色の「自動」バッジ。コレが自動調整適用済の状態。

f:id:neos21:20191026232529p:plain
↑タップすると自動調整が外れて元に戻る。

この写真について、「編集」メニューを押下し、トリミングモードに移ると、次のような挙動になると思う。

  • 上部・中央に「自動」のアイコンが出て、コレをタップすると黄色の「自動」アイコンに変化。青色の「自動」バッジのタップと同じように構図調整される
    • f:id:neos21:20191026232534p:plain
    • f:id:neos21:20191026232543p:plain
  • 右上の「3点リーダ 」アイコンをタップして表示されるメニューに「フレームの外側の撮影内容を使用」の項目がない
    • f:id:neos21:20191026232555p:plain

一見、フレームの外側の撮影内容を使えないように見えるが、実際は超広角レンズのデータがあり、それを併用して構図の自動調整はされる、とな。

カメラロールで写真の右上には星マークしかなく、編集時に自動調整ができないパターン

次からがよく分からないパターンたち。

カメラロールで写真を見ていると、右上に、点線で描かれた四角と、星 のマークの白いアイコンが表示されるモノがある。このアイコンは前述の「青色の自動バッジ」にも併記されているモノで、フレームの外側を超広角レンズで撮影したデータが存在することを表している。

f:id:neos21:20191026232626p:plain
↑コレの右上。四角と星のマークが付いている。

「編集」メニューを押下し、トリミングモードに移ると、次のような挙動になる。

  • 上部・中央に「自動」のアイコンが表示されていない (フレーム外の画像を使うよう調整すると「戻す」というボタンが出る)
    • f:id:neos21:20191026232644p:plain
    • f:id:neos21:20191026232651p:plain
  • 右上の「3点リーダ 」アイコンをタップして表示されるメニューに「フレームの外側の撮影内容を使用」の項目がない
    • f:id:neos21:20191026232659p:plain

カメラロールで写真の右上には星マークしかないが、編集時に自動調整ができるパターン

上と似ているが、編集画面で「自動」調整ができるパターン。

f:id:neos21:20191026232722p:plain
↑カメラロールでの見え方は同じ。

  • 上部・中央に「自動」のアイコンが表示されている
    • f:id:neos21:20191026232726p:plain
    • f:id:neos21:20191026232733p:plain
  • 右上の「3点リーダ 」アイコンをタップして表示されるメニューに「フレームの外側の撮影内容を使用」の項目がない
    • f:id:neos21:20191026232743p:plain

カメラロールで写真の右上には星マークしかないが、「フレームの外側の撮影内容を使用」ができるパターン

コレだけが「フレームの外側の撮影内容を使用」という項目が選べるパターン。

f:id:neos21:20191026232804p:plain
↑カメラロールでの見え方は上2つと同じ。青色の「自動」バッジは出ていない。

「編集」メニューを押下し、トリミングモードに移ると、次のような挙動になる。

  • 上部・中央に「自動」のアイコンが表示されている。タップすると黄色の「自動」アイコンに変化し、構図補正が行われる
  • 右上の「3点リーダ 」アイコンをタップして表示されるメニューに「フレームの外側の撮影内容を使用」の項目がある。タップした後は「オリジナルの撮影内容を使用」という表示に変わる
    • f:id:neos21:20191026232810p:plain
    • f:id:neos21:20191026232821p:plain

コチラも「編集」メニューには「自動」の項目があるのだが、カメラロール上では青色の「自動」バッジが表示されないのだ。

さらに、青色の「自動」バッジが表示されるパターンの写真には表示されなかった、「フレームの外側の撮影内容を使用」という項目が登場する。


これらの明確な違いがよく分からない、というお話だ。

フレームの外側を撮影するかどうかは、撮影環境の明るさが影響していそうだが、「必ず超広角レンズでも撮影しておく」ということはできない。それは分かった。

しかし、「編集」メニューに「自動」アイコンが表示されていて、自動で構図編集ができるというのに、青色の「自動」アイコンが表示されたりされなかったりするパターンがあるのは何なのか。コレがよく分からない。

何が違いの条件になるのか、分かった人がいたら教えてください。