この記事は
- スマホのブラウザで画像を圧縮する手順メモです
したかったこと
- スマホでブラウザから画像をアップロードさせたかったです
- 最近の高性能カメラだと画像サイズが大きすぎて非常に重くなってしまいました
- サーバサイドで圧縮する方式だと結局アップロードの通信量は変わらないから効果がありません
対処
- ブラウザ上で画像を圧縮して対応することにしました
- image-comporessorというJavaScriptライブラリを使います
- 内部的にはCanvasとか使って圧縮をしているみたいです
- 別にスマホじゃなくてもモダンブラウザなら動きます
対処コード
- FormDataオブジェクトを生成
- Ajax送信でのみ利用できる操作しやすいFormオブジェクトみたいなもの
- FormのDOMを渡して生成する
 
- 
input type=fileから画像ファイルオブジェクトを取得して、ImageCompressorに渡して圧縮- 圧縮に成功した場合は、FormDataオブジェクト内の画像データを圧縮後のものにすげかえる
- 圧縮処理は非同期になるので、複数圧縮する場合はPromiseに包んだ上で、Promise.allを使って同期的にする
 
- 普通にFormDataをajaxでPOSTする
ちなみにここでは、圧縮失敗した場合は元のデータのまま送信させたいので、rejectせずにresolveさせています
function postWithFiles(){
  // 画像圧縮処理を行う
  var fd = new FormData($('#form_id').get(0));
  var promises = []
  $('.file-input').each(function(index, input){
    // 画像圧縮のPromiseを画像数だけ生成
    var promise = new Promise(function(resolve, reject) {
      var file = input.files[0]
      if(file){
        const compressor = new ImageCompressor()
        compressor.compress(file, {
          quality: .6,
          width: 640,
          success(result) {
            // 圧縮前の画像データを除去する
            var inputName = $(input).attr('name');
            fd.delete(inputName)
            // 圧縮後の画像データを追加する
            fd.append(inputName, result, result.name)
            // 既存のアップロードinputのDOMも削除する
            input.remove();
            resolve();
          },
          error(e){
            // 圧縮失敗の場合はそのまま圧縮しない
            resolve()
          }
        })
      } else {
        // ファイルが選択されていない場合も一旦resolve
        resolve()
      }
    })
    promises.push(promise)
  })
  // 全てのPromise(=画像圧縮)に成功(resolve)の場合はPOSTする
  Promise.all(promises).then(function(values){
    $.ajax({
        url: "/xxx/create",
        type: "POST",
        data: fd,
        processData: false,
        contentType: false
    })
    .done(function( data ) {
        alert('保存完了!')
    });
  })
}
終わりに
- ブラウザで画像圧縮なんてできるわけないわーとか思ってたけどできたなー