web-dev-qa-db-ja.com

JQuery.ajaxでmultipart/formdataを送信する

私はjQueryのAjax関数を使用してサーバーサイドのPHPスクリプトにファイルを送信する問題を抱えています。 $('#fileinput').attr('files')でファイルリストを取得することは可能ですが、どのようにしてこのデータをサーバーに送信することが可能ですか?ファイル入力を使用する場合、サーバーサイドのphpスクリプトの配列($_POST)は0(NULL)です。

私はそれが可能であることを知っています(私は今までjQueryの解決策を見つけることはできませんでした、唯一のPrototyeコード( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress) html )).

これは比較的新しいように思われるので、ファイルのアップロードがXHR/Ajaxを介して不可能であることを言及しないでください。

Safari 5、FF、Chromeの機能がいいと思いますが、必須ではありません。

今の私のコードは次のとおりです。

$.ajax({
    url: 'php/upload.php',
    data: $('#file').attr('files'),
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    type: 'POST',
    success: function(data){
        alert(data);
    }
});
519
zoku

Safari 5/Firefox 4以降、FormDataクラスを使用するのが最も簡単です。

var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file-'+i, file);
});

これで、XMLHttpRequestと一緒に送信する準備ができたFormDataオブジェクトができました。

jQuery.ajax({
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
});

contentTypeオプションをfalseに設定してjQueryにContent-Typeヘッダーを追加しないように強制することが必須です。また、processDataフラグをfalseに設定したままにしておく必要があります。そうしないと、jQueryはFormDataを文字列に変換しようとしますが、失敗します。

これで、PHP内のファイルを取得できます。

$_FILES['file-0']

(ファイル入力でmultiple属性を指定していない限り、file-0というファイルは1つしかありません。

FormDataエミュレーションを使用する 古いブラウザ用 _

var opts = {
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
};
if(data.fake) {
    // Make sure no text encoding stuff is done by xhr
    opts.xhr = function() { var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; return xhr; }
    opts.contentType = "multipart/form-data; boundary="+data.boundary;
    opts.data = data.toString();
}
jQuery.ajax(opts);

既存のフォームからFormDataを作成

手動でファイルを繰り返すのではなく、既存のフォームオブジェクトの内容を使用してFormDataオブジェクトを作成することもできます。

var data = new FormData(jQuery('form')[0]);

カウンタの代わりにPHPネイティブ配列を使用

ファイル要素に同じ名前を付けて、名前を大括弧で終わらせるだけです。

jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file[]', file);
});

$_FILES['file']は、アップロードされたすべてのファイルのファイルアップロードフィールドを含む配列になります。繰り返しの方が簡単なので、最初のソリューションよりも実際にこれをお勧めします。

824

Raphaelのすばらしい答えに少しだけ追加したかったのです。 JavaScriptを使用して送信するかどうかに関係なく、PHPを取得して同じ$_FILESを生成する方法を次に示します。

HTMLフォーム:

<form enctype="multipart/form-data" action="/test.php" 
method="post" class="putImages">
   <input name="media[]" type="file" multiple/>
   <input class="button" type="submit" alt="Upload" value="Upload" />
</form>

JavaScriptなしで送信すると、PHPは次の$_FILESを生成します。

Array
(
    [media] => Array
        (
            [name] => Array
                (
                    [0] => Galata_Tower.jpg
                    [1] => 518f.jpg
                )

            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/jpeg
                )

            [tmp_name] => Array
                (
                    [0] => /tmp/phpIQaOYo
                    [1] => /tmp/phpJQaOYo
                )

            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )

            [size] => Array
                (
                    [0] => 258004
                    [1] => 127884
                )

        )

)

プログレッシブエンハンスメントを行う場合、RaphaelのJSを使用してファイルを送信します...

var data = new FormData($('input[name^="media"]'));     
jQuery.each($('input[name^="media"]')[0].files, function(i, file) {
    data.append(i, file);
});

$.ajax({
    type: ppiFormMethod,
    data: data,
    url: ppiFormActionURL,
    cache: false,
    contentType: false,
    processData: false,
    success: function(data){
        alert(data);
    }
});

...これは、そのJavaScriptを使用して送信した後のPHPの$_FILES配列の外観です。

Array
(
    [0] => Array
        (
            [name] => Galata_Tower.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpAQaOYo
            [error] => 0
            [size] => 258004
        )

    [1] => Array
        (
            [name] => 518f.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpBQaOYo
            [error] => 0
            [size] => 127884
        )

)

これはニースの配列であり、実際に一部の人々が$_FILESを変換するものですが、JavaScriptを使用して送信したかどうかに関係なく、同じ$_FILESで作業すると便利です。そのため、JSにいくつかの小さな変更を加えます。

// match anything not a [ or ]
regexp = /^[^[\]]+/;
var fileInput = $('.putImages input[type="file"]');
var fileInputName = regexp.exec( fileInput.attr('name') );

// make files available
var data = new FormData();
jQuery.each($(fileInput)[0].files, function(i, file) {
    data.append(fileInputName+'['+i+']', file);
});

(2017年4月14日編集:FormData()のコンストラクターからフォーム要素を削除しました。これにより、Safariでこのコードが修正されました。)

このコードは2つのことを行います。

  1. input name属性を自動的に取得し、HTMLをより保守しやすくします。これで、formのクラスがputImagesである限り、他のすべてが自動的に処理されます。つまり、inputには特別な名前を付ける必要はありません。
  2. 通常のHTMLが送信する配列形式は、data.append行のJavaScriptによって再作成されます。括弧に注意してください。

これらの変更により、JavaScriptで送信すると、単純なHTMLで送信する場合とまったく同じ$_FILES配列が生成されるようになりました。

47
ajmicek

私が読んだ情報に基づいてこの関数を構築したところです。

.serialize()を使うのと同じように使います。代わりに.serializefiles();を置くだけです。
私のテストではここで働いています。

//USAGE: $("#form").serializefiles();
(function($) {
$.fn.serializefiles = function() {
    var obj = $(this);
    /* ADD FILE TO PARAM AJAX */
    var formData = new FormData();
    $.each($(obj).find("input[type='file']"), function(i, tag) {
        $.each($(tag)[0].files, function(i, file) {
            formData.append(tag.name, file);
        });
    });
    var params = $(obj).serializeArray();
    $.each(params, function (i, val) {
        formData.append(val.name, val.value);
    });
    return formData;
};
})(jQuery);
44
evandro777

私のコードを見てください、それは私のために仕事をします

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );
43
Asad Malik

フォームがHTMLで定義されている場合は、イメージを繰り返し追加するよりもフォームをコンストラクタに渡す方が簡単です。

$('#my-form').submit( function(e) {
    e.preventDefault();

    var data = new FormData(this); // <-- 'this' is your form element

    $.ajax({
            url: '/my_URL/',
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',     
            success: function(data){
            ...
23
Devin Venable

Devin Venableの答え は私が望んでいたものに近かったが、複数のフォームで動作し、各ファイルが正しい場所に配置されるようにフォームで既に指定されているアクションを使用するものを望んだ。

.ready()を使わないように、jQueryのon()メソッドも使いたかった。

それで私はこれにたどり着いた:(あなたのjQueryセレクターでformSelectorを取り替えなさい)

$(document).on('submit', formSelecter, function( e ) {
        e.preventDefault();
    $.ajax( {
        url: $(this).attr('action'),
        type: 'POST',
        data: new FormData( this ),
        processData: false,
        contentType: false
    }).done(function( data ) {
        //do stuff with the data you got back.
    });

});
7
Karl Henselin

FormDataクラスは機能しますが、iOS Safari(少なくともiPhone上)では、Raphael Schweikertのソリューションをそのままでは使用できませんでした。

Mozilla DevにはNiceページ FormDataオブジェクトの操作について があります。

そのため、enctypeを指定して、ページのどこかに空のフォームを追加します。

<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo"></form>

次に、FormDataオブジェクトを次のように作成します。

var data = new FormData($("#fileinfo"));

そして Raphael's code のように進めてください。

1
topkara

上記のすべての解決策は見栄えがよくエレガントですが、FormData()オブジェクトはパラメータを想定していませんが、インスタンス化した後はappend()を使用してください。

formData.append(val.name、val.value);

1
szatti1489

IEの古いバージョンはFormDataをサポートしていません(FormDataの完全なブラウザサポートリストはこちら: https://developer.mozilla.org/en-US/docs/Web/API/FormData )。

Jqueryプラグイン(例: http://malsup.com/jquery/form/#code-samples )を使用するか、IFrameベースのソリューションを使用してajaxを介してマルチパートフォームデータを送信することができます。 https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

0
sudip

ファイル入力nameが配列を示し、multipleにフラグを立て、formを使用してFormData全体を解析する場合、入力を繰り返しappend()する必要はありません。ファイル。 FormDataは自動的に複数のファイルを処理します。

$('#submit_1').on('click', function() {
  let data = new FormData($("#my_form")[0]);

  $.ajax({
    url: '/path/to/php_file',
    type: 'POST',
    data: data,
    processData: false,
    contentType: false,
    success: function(r) {
      console.log('success', r);
    },
    error: function(r) {
      console.log('error', r);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="my_form">
  <input type="file" name="multi_img_file[]" id="multi_img_file" accept=".gif,.jpg,.jpeg,.png,.svg" multiple="multiple" />
  <button type="button" name="submit_1" id="submit_1">Not type='submit'</button>
</form>

button type="button"ではなく、通常のtype="submit"が使用されることに注意してください。これは、submitを使用してこの機能を取得することに依存していないことを示しています。

結果の$_FILESエントリは、Chrome devツールで次のようになります。

multi_img_file:
  error: (2) [0, 0]
  name: (2) ["pic1.jpg", "pic2.jpg"]
  size: (2) [1978036, 2446180]
  tmp_name: (2) ["/tmp/phphnrdPz", "/tmp/phpBrGSZN"]
  type: (2) ["image/jpeg", "image/jpeg"]

注:単一のファイルとしてアップロードした場合、一部の画像は正常にアップロードされる場合がありますが、複数のファイルのセットでアップロードした場合は失敗します。症状は、PHPが空の$_POSTおよび$_FILESを報告し、AJAXがエラーをスローしないことです。問題はChrome 75.0.3770.100およびPHP 7.0で発生します。私のテストセットでは、数十個の画像のうち1個でのみ発生するようです。

0
OXiGEN

Nova daysあなたもjQueryを必要としません:) フェッチAPIサポート表

let result = fetch('url', {method: 'POST', body: new FormData(documemt.querySelector("#form"))})
0
Alex Nikulin

今日私が遭遇した1つの落とし穴は、この問題に関連して指摘する価値があると思います:ajax呼び出しのためのURLがリダイレクトされるならば、content-type: 'multipart/form-data'のためのヘッダは失われることができます。

例えば、私は http://server.com/context?param=x に投稿していました

Chromeの[ネットワーク]タブで、このリクエストの正しいマルチパートヘッダーを確認しましたが、302 = http://server.com/context/?param=x にリダイレクトしています(コンテキストの後のスラッシュに注意してください)。

リダイレクト中にマルチパートヘッダーが失われました。これらの解決策がうまくいかない場合は、リクエストがリダイレクトされていないことを確認してください。

0
james