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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

JavaScript

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

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

Q&A

解決済

1回答

2281閲覧

[Javascript] Rails5でユーザーがトリミングした画像を、S3に直接アップロードしたい

tacro

総合スコア23

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

JavaScript

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

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

1グッド

1クリップ

投稿2018/04/01 02:09

編集2018/04/01 23:43

前提・実現したいこと

Rails5を使って、ユーザーが画像を選択し、自由にトリミングした上で投稿できる機能を実装中です。
Carrierwaveを用いた通常の実装はできたのですが、サーバーにherokuを使用しているのでアップロードが遅くなってしまうことを懸念し、jQuery file-uploadを使用して、クライアントサイドからS3に直接アップロードする方法を取ろうとしています。

画像をトリミングしない場合は問題なくこの方法で実装できたのですが、トリミングさせる際に、以下の問題が生じました。
JSの経験に乏しいので、ご助言いただきたいです…

参考記事:
Direct to S3 Image Uploads in Rails

Direct Upload of Image to S3 after Manual Cropping with Cropper.js
jQuery-File-Upload

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

⑴元の画像の代わりに、トリミングした画像データをs3にアップロードできません。
以下のようにコードを書いたのですが、トリミングされる前の画像データがアップされてしまいます。
【追記】2回submitボタンを押すと、2回目の送信ではトリミング後の画像がアップされることがわかりました。(詳細は後述します。)

⑵また、このコードだと、画像を選択し直した場合、投稿が二つ作成されてしまいます。

Javascript

1$(function() { 2 $('.directUpload').find("input:file").each(function(i, elem) { 3 var fileInput = $(elem); 4 var form = $(fileInput.parents('form:first')); 5 var submitButton = form.find('input[type="submit"]'); 6 var progressBar = $("<div class='bar'></div>"); 7 var barContainer = $("<div class='progress'></div>").append(progressBar); 8 fileInput.after(barContainer); 9 fileInput.fileupload({ 10 fileInput: fileInput, 11 url: form.data('url'), 12 type: 'POST', 13 autoUpload: false, 14 formData: form.data('form-data'), 15 paramName: 'file', // S3 does not like nested name fields i.e. name="user[avatar_url]" 16 dataType: 'XML', // S3 returns XML if success_action_status is set to 201 17 replaceFileInput: true, 18 acceptFileTypes: /(.|/)(gif|jpe?g|png)$/i, 19 20 add: function(e, data){ 21 if (data.files && data.files[0]) { 22 console.log("called"); 23 var reader = new FileReader(); 24 reader.onload = function(e) { 25 $('.preview').empty(); 26 $('.preview').append($('<img>').attr({// insert preview image 27 src: e.target.result, 28 id: "crop_img", 29 title: data.files[0].name 30 })); 31 $('#crop_img').cropper() // initialize cropper on preview image 32 }; 33 reader.readAsDataURL(data.files[0]); 34 }; 35 36 submitButton.on('click', function(){ 37 // crop のデータを取得 38 $('#crop_img').cropper('getCroppedCanvas').toBlob(function (blob){ 39 data.files[0] = new File([blob], data.files[0].name); 40 data.originalFiles[0] = data.files[0]; 41 }) 42 data.submit(); 43 }); 44 }, 45 46 progressall: function (e, data) { 47 var progress = parseInt(data.loaded / data.total * 100, 10); 48 progressBar.css('width', progress + '%') 49 }, 50 51 start: function (e) { 52 submitButton.prop('disabled', true); 53 54 progressBar. 55 css('background', 'black'). 56 css('display', 'block'). 57 css('width', '0%'). 58 text("Loading..."); 59 }, 60 61 done: function(e, data) { 62 submitButton.prop('disabled', false); 63 progressBar.text("Uploading done"); 64 65 // extract key and generate URL from response 66 var key = $(data.jqXHR.responseXML).find("Key").text(); 67 var url = '//' + form.data('host') + '/' + key; 68 69 // create hidden field 70 var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url }) 71 form.append(input); 72 $('.directUpload').submit(); //submit a form 73 }, 74 75 fail: function(e, data) { 76 submitButton.prop('disabled', false); 77 78 progressBar. 79 css("background", "red"). 80 text("Failed"); 81 } 82 }); 83 }); 84}); 85

Ruby

1#new.html.erb 2... 3<%= form_for @post, :url => { :controller => :posts, :action => :create } ,html: { class: "directUpload", data: { 'form-data' => (@s3_direct_post.fields), 'url' => @s3_direct_post.url, 'host' => URI.parse(@s3_direct_post.url).host } } do |f|%> 4 <div class="field" > 5 <%= f.label("アイテム画像(必須)") %> 6 <%= f.file_field :image_name %> 7 8 <div class="preview"><img id="crop_img" /></div> 9 <div class="clear_float"></div> 10 11 <%= f.hidden_field :designer_id, :value => current_user.id %> 12 <div class="clear_float"></div> 13 14 <%= f.label("コメント") %> 15 <%= f.text_area :detail %> 16 17 <%= f.submit("投稿", :id => "_submit") %> 18 <% end %> 19...

どうすれば、トリミング後の画像を、s3にアップすることができるでしょうか?
アドバイスをよろしくお願いいたします。

#試してみたこと
以下のようにコードを書き換えて、コンソールでオンクリックアクション時のdata.originalFiles[0]の中身を確認すると、ちゃんとトリミング後の画像になっていました。
一度submitボタンを押してアップロードが完了した後、もう一度クリックすると、トリミング後の画像がs3にアップされることがわかりました。
なぜ1度目のクリックで行かないのでしょうか…

Javascript

1$(function() { 2 $('.directUpload').find("input:file").each(function(i, elem) { 3 ... 4 submitButton.on('click', function(){ 5 // crop のデータを取得 6 $('#crop_img').cropper('getCroppedCanvas').toBlob(function (blob){ 7 data.files[0] = new File([blob], data.files[0].name); 8 data.originalFiles[0] = data.files[0]; 9 }) 10 data.submit(); 11 }); 12 }, 13 14 .... 15 16 done: function(e, data) { 17 ... 18 //アップロード完了時のフォームの提出をキャンセル 19 //$('.directUpload').submit(); //submit a form 20 }, 21 .... 22 23 24 }); 25}); 26

補足情報(FW/ツールのバージョンなど)

Ruby on Rails 5
aws-sdk (3.0.1, 2.11.25, 2.11.3)

defghi1977👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

toBlobメソッドの仕組みによる非同期処理の罠に嵌っています.

HTMLCanvasElement.toBlobメソッドは画像データBlobオブジェクトをコールバック関数を介して呼び出し元に返します.
ですから、あなたのコードのようにtoBlobメソッドを実行した直後submitを呼んでしまうと, まだ**Blobを作りきっていないにもかかわらずサーバーとの通信を開始**してしまいます.

この問題を解決するには, フォームの送信処理をtoBlobメソッドに渡しているコールバック関数の内部で呼び出すようにします.

NOTE:
なお、あなたのコードの全てを確認しているわけではないので、この修正が新たな問題を引き起こす可能性はあります.

$(function() { $('.directUpload').find("input:file").each(function(i, elem) { ... submitButton.on('click', function(){ // crop のデータを取得 $('#crop_img').cropper('getCroppedCanvas').toBlob(function (blob){ data.files[0] = new File([blob], data.files[0].name); data.originalFiles[0] = data.files[0]; data.submit();//とりあえずここでsubmitして動くか試してみる. }) //data.submit();//ここではまだBlobが取れていない }); }, .... done: function(e, data) { ... //アップロード完了時のフォームの提出をキャンセル //$('.directUpload').submit(); //submit a form }, .... }); });

NOTE:
もう一つの解決策としては, toBlobメソッドの呼び出しを画像のトリミング処理が行われる都度行なっておく方法があります.

投稿2018/04/01 23:56

defghi1977

総合スコア4756

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

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

tacro

2018/04/02 00:56

ありがとうございます!!! 実際に修正してみたところ、トリミング済みの画像が問題なく無事にアップされるようになりました! また別の問題が発生しましたが、そちらは別途質問を作成します。 本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問