Multi-File Uploaderって遅くないですか?

ツイッターのようなタイムライン機能を持ったアプリを作っているのですが、Multi-File Uploaderって画像のアップロードが遅いので困っています。

遅かったとしても、ローディング画像とかを表示させれればいいのですが、conditionalで’s loading がないので表示できません。

何か解決策はないでしょうか?

私の場合は、プラグインを使ってなんとかやってます。
画像はAWSのS3に保存になりますが、AWS File Uploaderがマルチファイル対応です。
それにProgressBarを組み合わせてます。

ご返信ありがとうございます!

そうなんですね!
どんな挙動なのかみたいのですが、見せていただくことは可能ですか?

画面収録-2021-05-24-13.51.36

そこから、さらにS3→imgixに画像をおくってサイズなどを最適化して表示しています。S3→imgixはほぼリアルタイムな体感速度です。

スクショありがとうございます!
なるほど…ちょっとアップロードが遅いですね :cry:

ちなみになんですけど、今は、このコードを使って画像をアップロードさせようと思っているのですが、ちょっと問題がありまして….

PCから画像をアップロードすると、正常に動作するのですが、スマホからだとおかしくなってしまいます。

DBには登録されるのですが、めちゃくちゃ遅くて、

Some items in your database exceed 10MB. This can slow down your application considerably. You should save this information as a file.

とメッセージが出てきます。

PCと同じ画像をスマホからアップロードするとこのメッセージがでるので、端末によって何かしらの影響がでていると思います。

これを解決する方法とかありますか? :disappointed_relieved:

うーん、ちょっとわからないです。。。
FileAPIを使って画像のローディング前にプレビューしてるのですね。これなら確かに高速ですね。

うまくいくかわからないですが、サーバーへの保存はimageデータにして、Base64でテキスト保存するのは回避してみてはどうですか?

そうですか、、、

imageデータにすると、DBに登録出来ないんです :disappointed_relieved:
なぜかはわからないですが、、、

JS to bubbleを使って「配列」をbubbleに受け渡すロジックが難しそうですね。

そういえば、AWS uploaderには画像をそのまま送信しないで、リサイズして使うオプションがありました。
スクリーンショット 2021-05-24 17.13.30

私の場合は、imgixをかましている関係で、リサイズなしでサーバー保存していますが、リサイズするとアップロード速度はそれなりに変化すると思います。
(ちなみに上のデモ映像は4000pxとかの画像も混じってます)

S3のイメージURLを同時に渡してくれるので、それを保存するのは用意です。あとはアップロードしているユーザーにはFileAPIからの画像を表示して、タイムラインにはサーバーからの画像を表示とかですかね。Twitterとかどうやってるんだろ。。。気になりますね。

AWS uploaderを使えば、僕のスマホ画像が重すぎる問題が解決するんですかね???

どうやら、このコード使えば画像の圧縮はできるんですけど、これはfor文とかで複数画像アップロードに対応するのがちょっとできる自信がないんですよね…

<!-- ファイル選択ボタン -->
<div style="width: 500px">
  <form enctype="multipart/form-data" method="post">
    <input type="file" name="userfile" accept="image/*" multiple>
  </form>
</div>

<!-- サムネイル表示領域 -->
<canvas id="canvas" width="0" height="0"></canvas>

<!-- アップロード開始ボタン -->
<button class="btn btn-primary" id="upload">投稿</button>

<!-- 以下、javascript -->
<script type="text/javascript">
$(function() {
  var file = null; // 選択されるファイル
  var blob = null; // 画像(BLOBデータ)
  const THUMBNAIL_WIDTH = 800; // 画像リサイズ後の横の長さの最大値
  const THUMBNAIL_HEIGHT = 800; // 画像リサイズ後の縦の長さの最大値

  // ファイルが選択されたら
  $('input[type=file]').change(function() {

    // ファイルを取得
    file = $(this).prop('files')[0];
    // 選択されたファイルが画像かどうか判定
    if (file.type != 'image/jpeg' && file.type != 'image/png') {
      // 画像でない場合は終了
      file = null;
      blob = null;
      return;
    }

    // 画像をリサイズする
    var image = new Image();
    var reader = new FileReader();
    reader.onload = function(e) {
      image.onload = function() {
        var width, height;
        if(image.width > image.height){
          // 横長の画像は横のサイズを指定値にあわせる
          var ratio = image.height/image.width;
          width = THUMBNAIL_WIDTH;
          height = THUMBNAIL_WIDTH * ratio;
        } else {
          // 縦長の画像は縦のサイズを指定値にあわせる
          var ratio = image.width/image.height;
          width = THUMBNAIL_HEIGHT * ratio;
          height = THUMBNAIL_HEIGHT;
        }
        // サムネ描画用canvasのサイズを上で算出した値に変更
        var canvas = $('#canvas')
                     .attr('width', width)
                     .attr('height', height);
        var ctx = canvas[0].getContext('2d');
        // canvasに既に描画されている画像をクリア
        ctx.clearRect(0,0,width,height);
        // canvasにサムネイルを描画

       ctx.drawImage(image,0,0,image.width,image.height,0,0,width,height);

        // canvasからbase64画像データを取得
        var base64 = canvas.get(0).toDataURL('image/jpeg');        
        // base64からBlobデータを作成
        var barr, bin, i, len;
        bin = atob(base64.split('base64,')[1]);
        len = bin.length;
        barr = new Uint8Array(len);
        i = 0;
        while (i < len) {
          barr[i] = bin.charCodeAt(i);
          i++;
        }
        blob = new Blob([barr], {type: 'image/jpeg'});
		  console.log(blob);
		  console.log(base64);
		  bubble_fn_base98_images(base64);

      }
      image.src = e.target.result;

    }
    reader.readAsDataURL(file);
  });


});
</script>

bubble_fn_base98_images(base64);
これはJStoBubbleのリストにチェック入ってます?

そもそもbubbleのストレージは10GBしかないので、ユーザーがアップロードしはじめると、すぐに追加しないといけないのでS3に しました。スマホとPCでAWSアップローダーは同じ挙動です。

プラグイン使うとノーコードに近いかたちで(AWSの設定はマニュアルに従って少しコードかく)実装できるので、その点は楽です。

リストにチェック入れたら動作しないんですよね…

AWSアップローダーを使うことを検討しないといけいかもですね…
ありがとうございます :sob:

ということはbase64が「配列」として出力されていないのかな…。
console.log(base64)で配列なのか否かは確認できますが、どんな様子ですか?

表示スピード優先なら、FileAPIを使ってアップロード前の画像データをプレビューさせるのは正しい選択に思えます。となるとFileAPIからのプレビュー画像を自身のタイムラインにも使用(仮表示)して、自身以外のタイムラインにはサーバーから呼び出した画像を表示させる仕分けが有効かもしれません。つまり、AWSに行く前にできることはあるかもです。あくまで仮説ですが。。。

似たような問題のスレッドみつけました。

  • Picture uploaderがいいみたい

APIを活用した別のアプローチもありました。

配列として認識させようとするとjavascripttobubbleがうまく動作しないっぽいんですよねー。この人の方法を使いました!

この人のこれを使ったらPCでは正しく動作するのですが、スマホでは画像が重すぎるっぽいんですよね。最近のスマホは高画質なので画像の容量が大きすぎるのが問題みたいです。

<script type="text/javascript">
    function readFile() {
        var base64_images = '';
        if (this.files) {
            var file_count = this.files.length;
            for (i = 0; i < this.files.length; i++) {
                var FR = new FileReader();
FR.onload = function(e) {
                    base64_images += e.target.result + '#';
                    if (file_count) {
bubble_fn_base64_images(base64_images);
console.log(base64_images);
                    }
                };
                FR.readAsDataURL(this.files[i]);
            }
        }
    }

    document.getElementById("file-input").addEventListener("change", readFile, false);
</script>

上記のやり方を、下の画像圧縮ができるコードに当てはめれたら完璧になると思うのですが…なかなかうまくできません :cold_sweat:

<!-- ファイル選択ボタン -->
<div style="width: 500px">
  <form enctype="multipart/form-data" method="post">
    <input type="file" name="userfile" accept="image/*" multiple>
  </form>
</div>

<!-- サムネイル表示領域 -->
<canvas id="canvas" width="0" height="0"></canvas>

<!-- アップロード開始ボタン -->
<button class="btn btn-primary" id="upload">投稿</button>

<!-- 以下、javascript -->
<script type="text/javascript">
$(function() {
  var file = null; // 選択されるファイル
  var blob = null; // 画像(BLOBデータ)
  const THUMBNAIL_WIDTH = 800; // 画像リサイズ後の横の長さの最大値
  const THUMBNAIL_HEIGHT = 800; // 画像リサイズ後の縦の長さの最大値

  // ファイルが選択されたら
  $('input[type=file]').change(function() {

    // ファイルを取得
    file = $(this).prop('files')[0];
    // 選択されたファイルが画像かどうか判定
    if (file.type != 'image/jpeg' && file.type != 'image/png') {
      // 画像でない場合は終了
      file = null;
      blob = null;
      return;
    }

    // 画像をリサイズする
    var image = new Image();
    var reader = new FileReader();
    reader.onload = function(e) {
      image.onload = function() {
        var width, height;
        if(image.width > image.height){
          // 横長の画像は横のサイズを指定値にあわせる
          var ratio = image.height/image.width;
          width = THUMBNAIL_WIDTH;
          height = THUMBNAIL_WIDTH * ratio;
        } else {
          // 縦長の画像は縦のサイズを指定値にあわせる
          var ratio = image.width/image.height;
          width = THUMBNAIL_HEIGHT * ratio;
          height = THUMBNAIL_HEIGHT;
        }
        // サムネ描画用canvasのサイズを上で算出した値に変更
        var canvas = $('#canvas')
                     .attr('width', width)
                     .attr('height', height);
        var ctx = canvas[0].getContext('2d');
        // canvasに既に描画されている画像をクリア
        ctx.clearRect(0,0,width,height);
        // canvasにサムネイルを描画

       ctx.drawImage(image,0,0,image.width,image.height,0,0,width,height);

        // canvasからbase64画像データを取得
        var base64 = canvas.get(0).toDataURL('image/jpeg');        
        // base64からBlobデータを作成
        var barr, bin, i, len;
        bin = atob(base64.split('base64,')[1]);
        len = bin.length;
        barr = new Uint8Array(len);
        i = 0;
        while (i < len) {
          barr[i] = bin.charCodeAt(i);
          i++;
        }
        blob = new Blob([barr], {type: 'image/jpeg'});
		  console.log(blob);
		  console.log(base64);
		  bubble_fn_base98_images(base64);

      }
      image.src = e.target.result;

    }
    reader.readAsDataURL(file);
  });


});
</script>

Picture uploaderを使おうと、思ったこともあるのですが、一枚ずつしかアップできないのでユーザーからしたらかなり不便だと思うんですよね…

ありがとうございます。参考にさせていただきます!

@yuta
これ使えるかも!マルチファイルにも対応してます。

3 Likes

おー!これマルチファイルもいけたんだ!見落としてました!!
ありがとうございます :joy:

これ使ってみます!!

1 Like