【Flutter】カメラで撮影した画像を端末内で縮小する

## はじめに
SNSなどのスマホアプリで、カメラで撮影した写真をクラウドのストレージサービスに格納するという仕組みはよくあると思います。
スマホのカメラは年々進化し、高画質な写真撮影が可能になりました。それに伴って写真1枚のファイルサイズが増大、それがストレージサービスの使用量増大に繋がり、結果サービス提供側のコスト増に繋がっています。また、利用者側（サービス授受側）では通信データ量が増大により、処理完了までの体感時間が遅くなる、通信上限に到達しやすくなるという問題が生じます。

上記サービス提供側、利用者両者の課題を解決するために、撮影した写真を端末内で縮小する機能が必要だと感じました。Flutterアプリでは「[flutter_image_compress](https://pub.dev/packages/flutter_image_compress)」を使用すると端末内で画像の縮小を簡単に実現できます。本記事ではその手順を紹介します。

## flutter_image_compressで画像を縮小する
導入手順や仕様、サンプルは[Github](https://github.com/OpenFlutter/flutter_image_compress)に詳細が記載いるため、深い内容を知りたい方は合わせて[Github](https://github.com/OpenFlutter/flutter_image_compress)をご覧ください。ここでは、さっと試せるように、最短の動作確認方法を説明します。

### flutter_image_compressの導入
Flutterを嗜まれている方であればおなじみの[pub.dev](https://pub.dev/)からコマンドで取得します。
pubspec.yamlに設定を追加して、`flutter pub get`を実行します。

```yaml:pubspec.yaml（dependenciesにflutter_image_compressを追加）
dependencies:
  flutter_image_compress: ^0.6.3
```

```shell:flutter_image_compressのインストール
flutter pub get
```

### flutter_image_compressの実装
実装例として、[ImagePicker](https://pub.dev/packages/image_picker)でカメラを起動し、撮影した画像を圧縮して端末に保存するというコードを紹介します。

```dart:flutter_image_compress実装例（全体）
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';   // ①

　　・・・

// ImagePickerでカメラを起動
File image = await ImagePicker.pickImage(source: ImageSource.camera);

// 画像が選択されなかった場合はスキップ
if (image == null) {
  return null;
}

// flutter_image_compressで指定サイズ／品質に圧縮
List<int> result = await FlutterImageCompress.compressWithFile(   // ②
  image.absolute.path,
  minWidth: 1536,
  minHeight: 1536,
  quality: 80,
);

// 端末の拡張ディスクに保存
String extStoPath = (await getExternalStorageDirectory()).path;
String saveFilePath =                                          
  extStoPath + 
  basenameWithoutExtension(image.absolute.path) + 
  '_compress' + 
  extension(image.absolute.path);
File saveFile = File(saveFilePath);
saveFile.writeAsBytesSync(result, flush: true, mode: FileMode.write);　// ③
```

flutter_image_compressに関連する①～③の個所について解説します。

```dart:①の部分
import 'package:flutter_image_compress/flutter_image_compress.dart';   // ①
```
ここではflutter_image_compressモジュールを読み込みます。

```dart:②の部分
List<int> result = await FlutterImageCompress.compressWithFile(   // ②
  image.absolute.path,
  minWidth: 1536,
  minHeight: 1536,
  quality: 80,
);
```

compressWithFileメソッドで、カメラで撮影した画像を縮小しています。`List<int> result`には、縮小後画像の画像情報が格納されます。resultのデータ長を取得することで、画像のデータ量を知ることができます。
compressWithFileメソッドでは、引数として与えられた条件で画像を縮小します。（縦横の最小値（minHeight/minWidth）、画像の品質（quality）です）。

いずれの引数も値を小さくすることで縮小後のファイルサイズは小さくなりますが、必要以上に縮小すると画像がつぶれてしまってうまく見えなくなってしまう恐れがあるため、縮小前後の画像を比較しながら適切な引数を設定しましょう。

```dart:③の部分
// 端末の拡張ディスクに保存
String extStoPath = (await getExternalStorageDirectory()).path;
String saveFilePath =                                          
  extStoPath + 
  basenameWithoutExtension(image.absolute.path) + 
  '_compress' + 
  extension(image.absolute.path);
File saveFile = File(saveFilePath);
saveFile.writeAsBytesSync(result, flush: true, mode: FileMode.write);　// ③
```

flutter_image_compressで得た縮小後の画像データを、File#writeAsBytesSyncで端末に保存しています。
今回はgetExternalStorageDirectoryメソッドを使用して、端末のストレージに画像データを書き込んでいます。

-----
下表が上記のコードを実行し、縮小前後のファイルサイズを計測したものです。
比較すると約1/10以下のファイルサイズになっています。縦横サイズの変更と合わせて画像（jpeg）の品質レベルを100->80に変更したことが効いているようです。

|縮小前|縮小後|
|---|---|
|2,024,923 Byte<br>（約2MB）|188,530 Byte<br>（約190KB）|

更にチューニングしたい場合は、画像とファイルサイズを見ながらcompressWithFileメソッドの引数をチューニングしましょう。パラメータの詳細は[Github](https://github.com/OpenFlutter/flutter_image_compress)の説明をご覧ください。

## おわりに
flutter_image_compressを用いると、画像の縮小が簡単にできることが伝わったでしょうか。
flutter_image_compressをググってみたのですが、日本語記事が全くないのですよね…（12/3現在）。これから始められる方の一助になれば幸いです。

今回は紹介を割愛しましたが、flutter_image_compressには他にも様々な調整パラメータがあります。詳細は[Github](https://github.com/OpenFlutter/flutter_image_compress)で解説されていますので、興味のある方は色々試してみてください。