質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.38%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Amazon S3

Amazon S3 (Simple Storage Service)とはアマゾン・ウェブ・サービスが提供するオンラインストレージサービスです。

Q&A

解決済

1回答

5753閲覧

LaravelからS3に画像を保存しようとするとエラーが出る

takaoS

総合スコア9

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Amazon S3

Amazon S3 (Simple Storage Service)とはアマゾン・ウェブ・サービスが提供するオンラインストレージサービスです。

0グッド

0クリップ

投稿2020/07/07 22:11

編集2020/07/07 23:52

実現したいこと

// 追記:すみません、書くべき情報が全く足りていなかったので、追加しました。

canvasで表示した画像をフォームでPOST送信し、その画像をS3バケットに保存したいです。

前提

ブラウザからカメラを起動して写真を撮影または選択し、それを正方形に切り抜いた画像をcanvasに表示させています。
送信ボタンを押すと、そのcanvasに表示されている画像をフォームに埋め込んで、POST送信する仕組みです。

画像の保存に関して、S3バケットではなく、Laravelの storage/app/public/ に保存できることは確認済みです(後述するControllerのコメントアウト部分)。

Laravel: ver.6.18.23

発生している問題・エラーメッセージ

ErrorException is_file() expects parameter 1 to be a valid path, string given

Contorollerの

$path = Storage::disk('s3')->putFile('/', $canvas, 'public');

の一文が関係していると思うのですが、ちゃんと第1引数にpathを指定しているので、原因がわからないです。

該当のソースコード

// view <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>カメラ サンプル</title> <meta name="description" content=""> <meta name="keywords" content=""> <meta name="csrf-token" content="{{ csrf_token() }}"> </head> <body> <h2>カメラサンプル</h2> <form id="image_form" action="{{ /*secure_*/url('/') }}" method="post"> @csrf <input id="input_image" type="file" style="display:none" name="input_image" accept="image/png, image/jpeg" capture> <input id="upload_image" type="hidden" name="upload_image" value=""> <input id="submit_image" type="submit" name="submit_image" value="送信"> </form> <canvas id="display" width="1500" height="1500" style="background-color: #000; width:250px; height:250px;"></canvas> <script src="{{ /*secure_*/asset('js/script.js') }}"></script> </body> </html>
// javascript var input_image = document.getElementById('input_image'); var display = document.getElementById('display'); var ctx = display.getContext('2d'); var canvas_width = 1500; var canvas_height = 1500; var canvas_style_width = 250; var canvas_style_height = 250; display.addEventListener('click', () => { input_image.click(); }, false) input_image.addEventListener('change', () => { var file = input_image.files[0]; var image = new Image(); var reader = new FileReader(); reader.onload = function() { image.onload = function() { var image_width = image.naturalWidth; var image_height = image.naturalHeight; // 縮小比の計算 var scale = 1.0; if ( (image_width/image_height) > (canvas_width/canvas_height) ) { scale = canvas_height / image_height; } else { scale = canvas_width / image_width; } // 元画像の切り出し領域の計算 var clip_width = canvas_width / scale; var clip_height = canvas_height / scale; var clip_x = (image_width - clip_width) / 2; var clip_y = (image_height - clip_height) / 2; // canvasに描画 ctx.clearRect(0, 0, canvas_style_width, canvas_height); ctx.drawImage(image, clip_x, clip_y, clip_width, clip_height, 0, 0, canvas_width, canvas_height); // canvasに描画した画像をjpegとしてformに埋め込み document.getElementById('upload_image').value = display.toDataURL('image/jpeg'); } image.src = reader.result; image.onerror = function (){ console.log(image.error); } } reader.readAsDataURL(file); reader.onerror = function (){ console.log(reader.error); } }, false);
// Controller <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; use App\Models\ImagePath; class CameraController extends Controller { public function index() { return view('capture_image'); } public function store(Request $request) { $canvas = $request->input('upload_image'); if (isset($canvas)) { $canvas = preg_replace('<data:image/jpeg;base64,>', '', $canvas); $canvas = base64_decode($canvas); //Storage::disk('public')->put('sample.jpeg', $canvas); $path = Storage::disk('s3')->putFile('/', $canvas, 'public'); $image_path = new ImagePath; //$image_path->path = asset('storage/sample.jpeg'); $image_path->path = Storage::disk('s3')->url($path); $image_path->save(); } return redirect(/*secure_*/url('/')); } public function index_video() { return view('capture_video_image'); } }

試したこと

AWSのEC2上でも試してみましたが、同じエラーが出ました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コードを実際に試したわけではないので、検討違いの回答でしたらご容赦を^^;


怒られているのは第2引数かなと思います。

laravelの中身を少し掘り下げて確認しましたが、putFileの、第二引数に渡せるのは、

①Fileクラス or UploadedFileクラスのインスタンス
②リソース
③ファイルパス(テキスト)

のいずれか。。だと思われます。

ご提示のコードで、$canvasはバイナリ文字列だと思うので、上記のどれにも該当しないのですが、

内部のフローが
if ($contents instanceof File ||
$contents instanceof UploadedFile) {
return $this->putFile($path, $contents, $options);
}
return is_resource($contents)
? $this->driver->putStream($path, $contents, $options)
: $this->driver->put($path, $contents, $options);
こうだったので、
①にも②にも該当しない$canvasを、ファイルパスとして処理しようと試みた結果のエラー・・・
ではないでしょうか。


すみません。。
手元にあったS3用ライブラリを入れていた環境のlaravelがちょっと古いバージョン(5.8くらいだった・・)でして、
そこでFileSystemの内部処理を見ていたことが裏目に出たようです。
laravel6あたりから、この内部処理がけっこう変わっていたことに気づかず、
勘違いをしてしまいました。。。

ということで、一部訂正します。

putFileを見てみると、

php

1 public function putFile($path, $file, $options = []) 2 { 3 $file = is_string($file) ? new File($file) : $file; 4 5 return $this->putFileAs($path, $file, $file->hashName(), $options); 6 }

こうなっていました。
バイナリ文字列は、is_string()trueになるので、
Fileクラスに渡されており、そのFileクラスの初期処理で

public function __construct(string $path, bool $checkPath = true) { if ($checkPath && !is_file($path)) { throw new FileNotFoundException($path); } parent::__construct($path); }

こう。。
エラー表示された is_file()が出てきました。。
つまり、ここでエラーになっているということになります。
(若干エラーの出し方が、laravelの意図した形にはなっていないようには見えますが)

なので、怒られている原因は、第2引数$canvasであることは、間違いなさそうです。

これらのコードを見る限り、第2引数に渡せるのは

①Fileクラス or UploadedFileクラスのインスタンス
③ファイルパス(テキスト)

の二択・・?(かの確信はない^^;)

少なくとも、->hashName()でエラーにならないクラスインスタンス等でなければ、
この先の処理には進めない、ということです。

投稿2020/07/08 05:49

編集2020/07/09 00:16
mix-peach

総合スコア1910

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takaoS

2020/07/08 14:25

ご回答いただきありがとうございます。 putFileの行のコードを以下に変更して試したところ、S3に保存できました。 $path = Storage::disk('s3')->put('sample.jpeg', $canvas); このことから、現在のままでの$canvasでも保存できるので、$canvasの型は関係ないのかと思います。 もしくは、putFile()の引数の順番が違うのでしょうか? 私の認識が違っていたらすみません。
takaoS

2020/07/09 02:57

回答の修正、ありがとうございます。 なるほど。。。 ということは、今のままでは$canvasを保存する前に、File() コンストラクタに引っかかってしまうわけですね。 $canvasをstring以外のものに変換できれば、putFile() が使えるということですかね...? $canvasを保存する、何かいい案がありましたらご教授いただけると幸いです。
mix-peach

2020/07/09 05:40

どうしてもputFileが使いたいのであれば、 一時ファイルに1回書き出して、そのパスを使って処理するのが手っ取り早そうですね。 もし、putでもいいなら、 画像パスをDBに保存している(ImagePath)のだと思うので、先にデータを作りそのIDを使ってファイル名を自分で定義する、という方法でもファイル名は重複しません。(put後にURLを取得してデータを更新する必要があること、ファイル名がすごく単純になること、、などが、仕様上よろしくない場合もあるかと思いますが・・)
takaoS

2020/07/09 08:11

なるほど、tmpfile()を使えばいいのですね! 大変勉強になりました。 丁寧に教えていただき、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.38%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問