0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Laravel × Flutter】FlutterアプリからLaravel経由でS3に画像をアップロードする

Posted at

ユーザー登録系のアプリでプロフィール画像を保存するためにFlutterからLaravel経由で画像をS3にアップロードする必要があったのですが、実装する時にかなり詰まったので投稿しました。

開発環境

Laravel Framework 7.30.6
Flutter 3.0.3

全体の流れ

全体の流れとしては

Flutter側で画像選択 → 選択した画像をbase64エンコードしてLaravel側に送信 → Laravel側でbase64エンコードされた画像を受け取ってデコード → S3にアップロード

という流れになります。

ちなみにですが、別にわざわざbase64エンコードしてテキストに変換してから送信しなくても画像アップロードはできると思うのですが、色々試してもうまくいかなかったので今回はbase64エンコードすることにしました。

(もっと簡単な方法があるならとても知りたい。。)

Flutterアプリ側の実装

画像のアップロードはimage_pickerを使用しました。

image_pickerを使うことでカメラロールから画像を選択したり、撮影した写真をアプリで使用することができます。

image_pickerの使用には色々と設定が必要なのですが、image_pickerの設定については別の記事を参考にしてください。

flutterアプリでカメラロールから画像を選択してアップロードする部分については下記のような形になります。

upload_image_sample.dart
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

class UploadImageSample extends StatefulWidget {
  const UploadImageSample({Key? key}) : super(key: key);

  @override
  UploadImageSampleState createState() => UploadImageSampleState();
}

class UploadImageSampleState extends State<UploadImageSample> {
  final picker = ImagePicker();
  File? image;

  Future<void> _setImage() async {
    final pickedFile = await picker.pickImage(source: ImageSource.gallery);

    setState(() {
      if (pickedFile != null) {
        image = File(pickedFile.path);
      }
    });
  }

  Future<void> _upload() async {
    if (image == null) return;

    // ここで画像をFileからBase64(String)にエンコードする
    Uint8List imageBytes = image!.readAsBytesSync();
    String imageBytesBase64 = base64Encode(imageBytes);

    // Laravel側のエンドポイントURL(環境に合わせて設定してください)
    Uri url = Uri.parse("https://hogegoge.jp");
    String body = jsonEncode({image: imageBytesBase64});

    // LaravelにPOST
    await http.post(url, body: body);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
      children: [
        // タップするとカメラロールが開いて画像を選択するボタン
        ElevatedButton(
            onPressed: () {
              _setImage();
            },
            child: const Text("画像選択")),
        // タップすると選択した画像をLaravelに送信するボタン
        ElevatedButton(
            onPressed: () {
              _upload();
            },
            child: const Text("アップロードする")),
      ],
    ));
  }
}

Laravel側の実装

次にLaravel側の実装です。
flutter側の実装で設定したURLを叩くと下記UploadController.phpのuploadメソッドが動く想定です。

UploadController.php
public function upload(Request $request){
  $image_base64 = $request->input("image");
  
  // 画像名をランダムで作成(本当は拡張子判定とかするんだろうけどとりあえずjpegで固定)
  $image_name = Str::random(20) . '.' . 'jpeg';

  // デコードする前に不要な文字列を置換する
  $image_base64 = str_replace('data:image/jpeg;base64,', '', $image_base64);
  $image_base64 = str_replace(' ', '+', $image_base64);
  $image_file = base64_decode($image_base64);

  // S3に保存
  $upload_path = "img/hogehoge"
  Storage::disk('s3')->put($upload_path, $image_file, 'public');

  // 読み取り用のURLを取得
  $url = Storage::disk('s3')->url($upload_path);

  // 取得したURLを返却
  return response()->json([
    "url" => $url,
  ]); 
}

これで無事アップロードができるはずです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?