MENU

IE9+対応でjQueryのajaxを使って非同期的にファイルをアップロードする

IE9以下でも画面遷移なしにファイルをアップロードする必要があり、少しつまづいたので紹介したいと思います。古くからある手法のようです。

ajaxでアップロード

HTML


わかりやすいように必要最低限のHTMLです。<button>type 属性は はJS側で送信するので、buttonとしておきます。

PHP


// 一時ファイルパス
$tmp = $_FILES['file']['tmp_name'];
// アップロードするフォルダのパス
$save = 'test/'.$_FILES['file']['name'];

// アップロードに成功したとき
if (@move_uploaded_file($tmp, $save))
{
  echo json_encode(array('state' => true, 'name' => $_FILES['file']['name']));
}
// アップロードに失敗したとき
else 
{
  echo json_encode(array('state' => false));
}

PHPも必要最低限しか書いていません。なので、自分で実装する際は、様々な脆弱性に対して対策する必要があります。処理結果やファイル名など、自分が返したいデータをJSON化して表示させます。

JS


$('.button').on('click', function()
{
  $.ajax(
  {
    type: 'post',
    url : 'ajax-post-file-up.php',
    data: new FormData($('.form')[0]),
    cache: false,
    contentType: false,
    processData: false
  })
  .done(function(response)
  {
    var data = JSON.parse(response);

    if (data['state'])
    {
      console.log('アップロードが完了しました');
      console.log('アップロードしたファイル:' + data['name']);
    }
    else 
    {
      console.log('アップロードが失敗しました');
    }
  })
  .fail(function()
  {
    console.log('ajax通信失敗');
  });
});

ボタンがクリックされたら $.ajax でファイルを送信します。レスポンスを受け取ったら JSON.parse() して、文字列をJSONとして扱えるようにします。あとは、PHPで出力させたデータを好きなように処理するだけです。

しかし、1つだけ問題があります。new FormDataIE10+にしか対応していません。自分の場合は、現在でも案件ではIE9も対応させています。そこで、IE9以下にも対応する方法を紹介したいと思います。

IE9以下にも対応

HTMLPHPは先ほどと同じです。


// FromDataに対応していないとき(IE9-)
if (!window.FormData)
{
  // 見えないiframeを生成
  $('.form').append('');
  // ファイル送信の際に必要な属性を追加
  $('.form').attr(
  {
    'action': 'ajax-post-file-up.php',
    'method': 'post',
    'enctype': 'multipart/form-data',
    'target': 'ie9_ajax_like'
  });
  
  // フォームが送信されたとき
  $('.form').on('submit', function()
  {
    // iframe内にレスポンスが返ってきたとき
    $('.iframe').off().on('load', function()
    {
      // body内のレスポンステキスト(JSON文字列)を取得
      var response = $('.iframe').contents().find('body').text();
      // JSON化
      var data = JSON.parse(response);
      
      // JSONのパラメータを使ってやりたい処理を書く
      if (data['state'])
      {
        console.log('アップロードが完了しました');
        console.log('アップロードしたファイル:' + data['name']);
      }
      else
      {
        console.log('アップロードが失敗しました');
      }
    });
  });
}

$('.button').on('click', function()
{
  // FormDataに対応しているとき(IE10+)
  if (window.FormData)
  {
    $.ajax(
    {
      type: 'post',
      url : 'ajax-post-file-up.php',
      data: new FormData($('.form')[0]),
      cache: false,
      contentType: false,
      processData: false
    })
    .done(function(response)
    {
      // JSON化
      var data = JSON.parse(response);

      // JSONのパラメータを使ってやりたい処理を書く
      if (data['state'])
      {
        console.log('アップロードが完了しました');
        console.log('アップロードしたファイル:' + data['name']);
      }
      else 
      {
        console.log('アップロードが失敗しました');
      }
    })
    .fail(function()
    {
      console.log('ajax通信失敗');
    });
  }
  // 対応していないとき(IE9-)
  else
  {
    // 普通にform送信
    $('.form').submit();
  }
});

IE9以下の場合は普通に <form> で送信します。しかし、そのままでは submit ボタンをクリックしたときに画面遷移してしまいます。なので、display:none<iframe> を追加してその中にデータを受け取ります。<form> タグには target 属性があり、これを <iframe>name 属性と同じにすることで連携させることができます。この手法は以下の記事で紹介されています。

https://havelog.ayumusato.com/develop/javascript/e171-ajax-file-upload.html