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


563

jQueryのajax関数を使用してサーバーサイドのPHPスクリプトにファイルを送信するときに問題が発生しました。ファイルリストを取得する$('#fileinput').attr('files')ことは可能ですが、このデータをサーバーに送信するにはどうすればよいですか?ファイル入力を使用する$_POSTと、サーバーサイドのphp-script の結果の配列()は0(NULL)になります。

私はそれが可能であることを知っています(今までjQueryソリューションは見つかりませんでしたが、Prototyeコードのみ(http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html) )。

これは比較的新しいようですが、XHR / Ajaxを介したファイルのアップロードは確実に機能しているため、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);
    }
});

残念ながら、FormDataオブジェクトの使用はIE <10では機能しません。
アレハンドロガルシアイグレシアス2013年

@GarciaWebDevおそらく、Flashでポリフィルを使用して同じAPIをサポートできます。詳細については、github.com / Modernizr / Modernizr / wiki /…をご覧ください。
yuxhuang 2013


3
を使用$(':file')して、すべての入力ファイルを選択できます。それは少し簡単です。
Shahar

@RameshwarVyevhareその回答は、この質問が回答されてから5年後に投稿されました。自分の答えを宣伝するためだけに同様の質問をしないでください。
Ryan P

回答:


882

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

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

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

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']

(ファイル入力で属性file-0を指定しない限り、ファイルは1つだけmultipleです。この場合、ファイルごとに番号が増加します。)

古いブラウザでの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']アップロードされるすべてのファイルのファイルアップロードフィールドを含む配列になります。反復する方が簡単なので、最初のソリューションよりも実際にこれをお勧めします。


2
また、このソリューションを古いブラウザに簡単に移植できるFormDataエミュレーションもあります。必要なのはdata.fakecontentTypeプロパティを手動で確認して設定することと、xhrをオーバーライドして使用することだけsendAsBinary()です。
Raphael Schweikert、2011

13
あなたはクラスも使用してjQueryおらず、FormDatajQueryに固有の質問とFormDataの使用に固有の回答のコンテキストで私に質問していますか?申し訳ありませんが、私はあなたを助けることができないと思います…
Raphael Schweikert

3
これは使用しapplication/x-www-form-urlencodedます。multipart/form-data代わりに使用する方法はありますか?
Timmmm 2013

4
@Timmmmいいえ、使用しmultipart/form-dataます。使用してapplication/x-www-form-urlencodedも機能しません。
Raphael Schweikert 2013

5
@RoyiNamirそれはコードでのみ文書化されています、私は恐れています。
Raphael Schweikert 2014年

51

ラファエルの素晴らしい答えに少し追加したかっただけです。$_FILESJavaScriptを使用して送信するかどうかに関係なく、PHPで同じを生成する方法は次のとおりです。

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>

PHPは$_FILES、JavaScriptなしで送信されるとこれを生成します。

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);
    }
});

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

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するものですが$_FILES、JavaScriptを使用して送信したかどうかに関係なく、同じ配列で作業すると便利です。そこで、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たHTMLは、より保守作り、自動的にname属性を。これでform、クラスputImagesがある限り、他のすべてが自動的に処理されます。つまり、input特別な名前を付ける必要はありません。
  2. 通常のHTMLが送信する配列形式は、data.append行のJavaScriptによって再作成されます。括弧に注意してください。

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


Safariでも同じ問題がありました。ヒントをありがとう!
medoingthings

49

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

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

これは完全に機能し、実装は非常に簡単です。
Jh62

45

私は読んだ情報に基づいてこの関数を作成しました。

使用するのと同じように使用します。.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);

2
私はこれを機能させようとしましたが、この定義がページの先頭にあるにもかかわらず、serializefiles()を関数として認識しないようです。
Fallenreaper 2012

1
それは私にとってはうまくいきます。var data = $("#avatar-form").serializefiles();これをajaxデータパラメータで送信してデータを取得 し、express form.parse(req, function(err, fields, files){
formididableで

23

フォームが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){
            ...

9

Devin Venableの答えは私が望んでいたものに近かったのですが、複数のフォームで機能し、フォームで既に指定されているアクションを使用して各ファイルが適切な場所に移動するものを求めていました。

.ready()の使用を回避できるように、jQueryのon()メソッドも使用したいと思いました。

これは私にこれをもたらしました:(formSelectorをjQueryセレクターに置き換えます)

$(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.
    });

});

2

最近ではjQueryも必要ありません:) fetch APIサポートテーブル

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

経由でファイルアップロードのコンテキストでfetch:互換性を参照してくださいdeveloper.mozilla.org/en-US/docs/Web/API/...
オマール・タリク

@OmarTariqはい私は私の回答に同様のリンクがあります))
Alex Nikulin

おっとっと。どうすればそれを見逃すことができます:-|
Omar Tariq

1

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

Mozilla Devには、FormDataオブジェクトの操作に関する素晴らしいページがあります

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

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

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

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

ラファエルのコードのように進んでください。


Safariでサイレントにハングアップするjquery ajaxアップロードに問題があり、IE9とFF18で動作するajaxアップロードの代わりに、Safariに対してブラウザーの条件付き$( 'form-name')。submit()を実行することになりました。おそらくマルチアップロードの理想的な解決策ではありませんが、jqueryダイアログからiframeへの単一ファイルに対してこれを行っていたため、問題なく動作しました。
グリフ2013年

1

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

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

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

リダイレクト中にマルチパートヘッダーが失われました。これらのソリューションが機能しない場合は、リクエストがリダイレクトされないようにしてください。


1

上記のすべてのソリューションは見栄えが良くエレガントですが、FormData()オブジェクトはパラメーターを想定していませんが、インスタンス化した後にappend()を使用します。

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


1

ファイル入力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開発ツールでは次のようになります。

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"]

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


0

古いバージョンの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

PHP

<?php 
    var_dump( $_FILES ); 
?>

jQueryとフォームHTML

$( "#form" ).bind( "submit", function (e) {

    e.preventDefault();

    $.ajax({

        method: "post",
        url: "process.php",

        data: new FormData( $(this) ), // $(this) = formHTML element

        cache: false,

        /* upload */
        contentType: false,
        processData: false

    });

} )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<form id="form" >
    File: <input type="file" name="upload" />
    <input type="submit" value="Submit" />
</form>


-1
  1. jquery-> $( "#id")[0]によってフォームオブジェクトを取得する
  2. data = new FormData($( "#id")[0]);
  3. わかりました、データはあなたの望みです

$( "#id")[0]は、フォームの最初の何もない空の<input type = "file" />を返します。フォームのすべての<input type = "file" />を含むフォーム全体をどのように送信しますか?
Mohammad-Hossein Jamali
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.