Closed3

「ブラウザからファイルアップロードする際にプログレスバーを表示したい」調べたことまとめ

ハトすけハトすけ

一番カンタンな方法

axiosの第3引数にoptionを渡す。コードはaxiosのgithubから抜粋

document.getElementById('upload').onclick = function () {
    var data = new FormData();
    data.append('foo', 'bar');
    data.append('file', document.getElementById('file').files[0]);

    var config = {
        onUploadProgress: function(progressEvent) {
            var percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total );
        }
    };

    axios.put('/upload/server', data, config)
        .then(function (res) {
            output.className = 'container';
            output.innerHTML = res.data;
        })
        .catch(function (err) {
            output.className = 'container text-danger';
            output.innerHTML = err.message;
        });
};

https://github.com/axios/axios/blob/master/examples/upload/index.html
https://www.youtube.com/watch?v=Ti8QNiRRzOA

ハトすけハトすけ

websocketを使った方法

僕自身が実現したかったのは、websocketを使ってファイルをアップロードする際に、プログレスバーを表示したかったです。

調べたところ以下のstackoverflowが詳しかったです。
https://stackoverflow.com/questions/11080112/large-file-upload-with-websocket

結論からいうと、websocketを利用して、onUploadProgress のような進捗コールバックを取得するよい方法はなさげです。そこで上記のstackoverflowは次のような提案をしていました。

  • ファイルアップロードはwebsocketではなくHTTPリクエスト(axiosなど)を利用する
  • 次のようにフロントでFileもしくはBlobをsliceして分割アップロードする。サーバー側ではアップロード完了次第終わったことをemitし、フロントに告げる。フロントは終わったことを知って、次のアップロードをする。
function Uploader(url, file) {
    var fs = new FileSlicer(file);
    var socket = new WebSocket(url);

    socket.onopen = function() {
       socket.send(fs.getNextSlice());
    }
    socket.onmessage = function(ms){
        if(ms.data=="ok"){
           fs.slices--;
           if(fs.slices>0) socket.send(fs.getNextSlice());
        }else{
           // handle the error code here.
        }
    }
}

上記のコードはstackoverflowから持ってきました。ここでのFileSlicerはどのような実装かは述べられていませんでした...。

あと、File.sliceというメソッドがちらほら検索にひっかかるけど、探しても出てこないので、一旦Blobにしてからblob.sliceをしないといけないかもしれません。

追記:

すみません、File..sliceというメソッドがないみたいな嘘ついてしまいました。ありました。
どうやらBlobから継承しているみたいです。

https://developer.mozilla.org/ja/docs/Web/API/File

ハトすけハトすけ

AWS S3を使った方法

aws-sdk をつかってブラウザからアップロードする状況を考えます。

AWS.S3.managedUploadを使うと実現できるみたいです。AWS.S3.uploadという構文もあるみたいです。
コードの検証はしてません。

function addPhoto(albumName) {
  var files = document.getElementById("photoupload").files;
  if (!files.length) {
    return alert("Please choose a file to upload first.");
  }
  var file = files[0];
  var fileName = file.name;
  var albumPhotosKey = encodeURIComponent(albumName) + "/";

  var photoKey = albumPhotosKey + fileName;

  // Use S3 ManagedUpload class as it supports multipart uploads
  var upload = new AWS.S3.ManagedUpload({
    params: {
      Bucket: albumBucketName,
      Key: photoKey,
      Body: file,
      ACL: "public-read"
    }
  });

  サンプルコードをコメントアウト
  // var promise = upload.promise();

  おそらくここを次のようにすれば取得できそう
  var promise = upload.on('httpUploadProgress', (progress) => {
    console.log("Uploaded :: " + parseInt((progress.loaded * 100) / progress.total)+'%');
  }).promise();

  promise.then(
    function(data) {
      alert("Successfully uploaded photo.");
      viewAlbum(albumName);
    },
    function(err) {
      return alert("There was an error uploading your photo: ", err.message);
    }
  );
}

ドキュメントによれば、このhttpUploadProgress イベントは複数のアップロードをまとめて一つのイベントにしているみたいです。

https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html

https://docs.aws.amazon.com/ja_jp/AWSJavaScriptSDK/latest/AWS/S3/ManagedUpload.html

https://stackoverflow.com/questions/58106792/aws-sdk-javascript-promise-with-callback-httpuploadprogress

このスクラップは2020/12/03にクローズされました