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

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

ただいまの
回答率

90.04%

【スマホ対応】Javascriptからs3に画像をアップすると、ファイルの中身が空になってしまう問題を解決したい

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,170

tacro

score 17

 前提・実現したいこと

Rails5で開発をしています。
jQuery file uploadを使用して、ユーザーが画像をトリミング後、クライアント側からS3へ直接画像をアップロードする機能を実装しています。
PC(MacBook Pro)のchromeからは問題なくアップロードができるのですが、PCとiPhoneのsafari、およびiPhoneのchromeからアップすると、ファイルの中身が空っぽになってしまう問題が発生しています。

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

トリミングをしない通常のアップロードはブラウザに関係なく行えるのですが、トリミング後の画像をキャンバスで取得し、blobファイルに変換してアップしようとすると、ブラウザによってはアップロードがうまくいきません。
s3へのファイルのアップロード自体は問題なくできている(指定したディレクトリに、指定した名前でファイルが作成されている)のですが、サイズが0Byteになってしまっています。
一通り調べたのですが、同じような問題を見つけることができず、原因に見当がつきません。
blobファイルの作成がうまくいっていないのでしょうか?
アドバイスをよろしくお願いいたします。

 該当のソースコード

Direct to S3 Image Uploads in Rails を参考に、Javascriptを以下のように書いています。

$(function() {
  $('.directUpload').find("input:file").each(function(i, elem) {
    var fileInput    = $(elem);
    var form         = $(fileInput.parents('form:first'));
    var submitButton = form.find('input[type="submit"]');
    var progressBar  = $("<div class='bar'></div>");
    var barContainer = $("<div class='progress'></div>").append(progressBar);
    fileInput.after(barContainer);
    fileInput.fileupload({
    fileInput:       fileInput,
    url:             form.data('url'),
    type:            'POST',
    autoUpload:       false,
    formData:         form.data('form-data'),
    paramName:        'file', // S3 does not like nested name fields i.e. name="user[avatar_url]"
    dataType:         'XML',  // S3 returns XML if success_action_status is set to 201
    replaceFileInput: false,
    acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
    // singleFileUploads: false,
    maxNumberOfFiles: 1,
    sequentialUploads: true,
    limitMultiFileUploads:1,
    limitConcurrentUploads: 1,

    add: function(e, data){
      if (data.files && data.files[0]) {
        var reader = new FileReader();
        reader.onload = function(e) {
          $('.preview').empty();
          $('.preview').append($('<img>').attr({// insert preview image
            src: e.target.result,
            id: "crop_img",
            title: data.files[0].name
          }));
          $('#crop_img').cropper() // initialize cropper on preview image
         };
       reader.readAsDataURL(data.files[0]);
        };

      submitButton.on('click', function(){
      $('form').submit(function(){
        return false;
        });
       // 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();
        })
     });
    },

    progressall: function (e, data) {
      var progress = parseInt(data.loaded / data.total * 100, 10);
      progressBar.css('width', progress + '%')
    },

    start: function (e) {
      submitButton.prop('disabled', true);

      progressBar.
        css('background', 'black').
        css('display', 'block').
        css('width', '0%').
        text("Loading...");
    },

    done: function(e, data) {
      submitButton.prop('disabled', false);
      progressBar.text("Uploading done");

      // extract key and generate URL from response
      var key   = $(data.jqXHR.responseXML).find("Key").text();
      var url   = '//' + form.data('host') + '/' + key;

      // create hidden field
      var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url })
      form.append(input);

      //delete submit event which is false
      $('form').off('submit');
      //and submit again
      $('.directUpload').submit();
    },

    fail: function(e, data) {
      submitButton.prop('disabled', false);

      progressBar.
        css("background", "red").
        text("Upload Failed!");
      }
    });
  });
});

 試してみたこと 1

JavaScript-Canvas-to-Blobをviewに付け足してみましたが、変わりありませんでした。
テストサイトにアクセスして、このpolyfill自体がiOSのsafari上で動くことは確認できました

 試してみたこと 2

toBlob() メソッドを使う代わりに、以下のコードで試してみましたが、変わりありませんでした…

      // 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();
     //  })
   //以上のコードを以下に置換
     var canvas = $('#crop_img').cropper('getCroppedCanvas');
     var canvas_data = canvas.toDataURL();
     var blobData = dataURItoBlob(canvas_data);
     function dataURItoBlob(dataURI) {
        var binary = atob(dataURI.split(',')[1]);
        var array = [];
        for(var i = 0; i < binary.length; i++) {
            array.push(binary.charCodeAt(i));
        }
        return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
    }
    data.files[0] = new File([blobData], data.files[0].name);
    data.originalFiles[0] = data.files[0];
    data.submit();

 試してみたこと 3

safariのコンソールでdata.submit()直前のdata,data.files[0],data.filesの中身を確認してみたところ、それぞれ正しく中身が確認されました。
fileuploadライブラリのsubmit()メソッドの問題かもしれません。

$('#crop_img').cropper('getCroppedCanvas').toBlob(function (blob){
            data.files[0] = new File([blob], data.files[0].name);
            data.originalFiles[0] = data.files[0];
            console.log(data.files[0]);
            console.log(data);
            console.log(data.files);
            // at this point the data is correct on both browsers
            data.submit();
            // but submitted file is empty
          })

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

Ruby on Rails 5
サーバー:heroku hobby
試した機種とブラウザ:
[MacBook Pro]
Chrome:問題なく成功
Safari:アップロードはされているが、ファイルの中身が空

[iPhone 7]
Chrome:アップロードはされているが、ファイルの中身が空
Safariアップロードはされているが、ファイルの中身が空

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正の依頼

  • h_daido

    2018/04/16 20:20

    問題の切り分けが必要そうですすね。iphone7をMacBookとつなげて、safariのwebインスペクタを起動できますか?networkタブでs3に対するリクエストとレスポンスが見れるはずなので、そこの値を確認してみると良いかと思います。

    キャンセル

まだ回答がついていません

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

  • ただいまの回答率 90.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • JavaScriptに関する質問
  • 【スマホ対応】Javascriptからs3に画像をアップすると、ファイルの中身が空になってしまう問題を解決したい