1. 程式人生 > >基於jQuery的ajax系列之用FormData實現頁面無重新整理上傳

基於jQuery的ajax系列之用FormData實現頁面無重新整理上傳

接著上一篇ajax系列之用jQuery的ajax方法向伺服器發出get和post請求寫,這篇主要寫如何利用ajax和FormData實現頁面無重新整理的檔案上傳效果,主要用到了jQuery的ajax()方法和XMLHttpRequest Level 2的FormData介面。關於FormData,大家可以看MDN文件

1,先看效果圖

期望的功能和效果很簡單:點選頁面中的上傳檔案表單控制元件,選擇檔案後點擊“ajax提交”,將檔案上傳至伺服器,上傳成功後,頁面給出一個簡單的提示。

2,前端的程式碼

看下html程式碼:

<div class="box">
            <div>
                <div class="item">
                    <input type="file" name="myfile" style="font-size: 0.7rem;">
                </div>
                <div class="item">
                    <button type="button" style="display: block; padding: 4px 18px;" class="btn-default">ajax提交</button>
                </div>
                <div class="item">
                    <button type="submit" style="display: block; padding: 4px 18px;" class="btn-default">form提交</button>
                </div>
            </div>
            <div class="prompt" style="font-size: 0.7rem;"></div>
        </div>
        <script src="../../js/jquery-3.1.0.min.js"></script>
        <script src="upload01.js"></script>

程式碼很簡單,需要注意的是頁面中沒有用到form表單,那麼怎麼提交資料呢,答案是用FormData來模擬表單中的 <input type="file"name="myfile"> 控制元件。另外,頁面中的樣式沒有寫出來。下面來看下html中引入的upload01.js程式碼,這個是重點。

upload01.js程式碼:

$(function($) {
    $('input[name=myfile]').on('change', function(e) {
        $('button[type=button]').on('click', function(e) {
            var formData = new FormData();
            // formData.ppend(name, element);
            formData.append('myfile', $('input[name=myfile]')[0].files[0]);
            $.ajax({
                url: 'upload01.php',
                method: 'POST',
                data: formData,
                contentType: false, // 注意這裡應設為false
                processData: false,
                cache: false,
                success: function(data) {
                    if (JSON.parse(data).result == 1) {
                        $('.prompt').html(`檔案${JSON.parse(data).filename}已上傳成功`);
                    }
                },
                error: function (jqXHR) {
                    console.log(JSON.stringify(jqXHR));
                }
            })
            .done(function(data) {
                console.log('done');
            })
            .fail(function(data) {
                console.log('fail');
            })
            .always(function(data) {
                console.log('always');
            });
        });
    });
});

(1) 下面解釋下FormData,涉及到了程式碼的第4、6、10行。

第4行 var formData = new FormData(); 例項化了一個空的FormData物件,可以認為它就是一個form表單,但現在裡面什麼控制元件都沒有。

第6行 formData.append('myfile', $('input[name=myfile]')[0].files[0]); ,給例項化的formdata物件新增一個控制元件,注意這裡新增的是已有控制元件 <input type="file" name="myfile" style="font-size: 0.7rem;"> (見html程式碼第4行)。

FormData.append(name, value, filename)方法可以很方便的以“鍵-值”對的形式給FormData新增控制元件,注意第3個引數“上傳文名”是可選的,它的具體語法和用法見FormData

第10行,將例項化的formData作為jQuery.ajax()方法data引數的值傳遞進去,提交給後端伺服器。

(2) 再解釋下ajax()方法中的contentType、processData引數。

contentType引數,發起http請求時設定的請求頭中的contentType。jQuery.ajax()預設的值為:'application/x-www-form-urlencoded; charset=UTF-8',這個在大多數情況下都是適用的。

但經過測試,保持預設時會報錯,因為傳送的資料中有input type="file"(上傳檔案),那麼這時contentType應該設定為'multipart/form-data',但如果指定為這個型別服務端(php)就會報這個錯誤: Warning: Missing boundary in multipart/form-data POST data in Unknown on line 0 。具體原因現在還不清楚,所以這裡先將contentType設定為false,即不讓jQuery設定contentType。

processData引數,根據jQuery.ajax()文件中的解釋,預設情況下,jQuery會處理髮送的資料,將資料按照'application/x-www-form-urlencoded'的要求轉換為查詢字串,如果要傳送的資料是DOMDocument或者不需要處理,就可以設定為false不讓jQuery轉換資料,我們這裡要傳送的資料其實就是DOMDocument。

經過測試,如果保持預設(true)的話,在發起請求前js會報錯: TypeError: 'append' called on an object that does not implement interface FormData. 

另外還有個dataType引數,期望從伺服器中返回的資料格式,這裡最好也不要指定,而是讓jquery自己根據http響應頭中的contentType判斷,然後返回一個合適的資料型別。指定後不會影響後臺程式的邏輯處理,但你在前端接收的資料很可能不是期望的資料,於是js就會報這一類錯誤: SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data ,這個是將dataType指定為json後報的錯誤。

3,後端的php程式碼

後端伺服器是nginx,用php來處理髮送過來的資料,程式碼也很簡單:

<?php
// var_dump($_REQUEST); // 為空陣列
// var_dump($_FILES); //不為空

// 當使用FormData配合ajax上傳檔案時,$_REQUEST、$_POST都是空陣列,php://input也是null
if (isset($_FILES['myfile']) && !empty($_FILES['myfile'])) {
    if (move_uploaded_file($_FILES['myfile']['tmp_name'], $_FILES['myfile']['name'])) {
        echo '{"result": 1, "filename": "' . $_FILES['myfile']['name'] . '"}';
    } else {
        echo '{"result": 0}';
    }
}

程式碼的邏輯很簡單這裡就不多解釋了。主要說下我在除錯程式時遇到的問題,遇到的問題總結起來就一句話:當使用FormData配合ajax上傳檔案時,$_REQUEST、$_POST都是空陣列,php://input也是null。可以看到,我在程式碼中的第2、3、5行也寫了相關的註釋。為什麼$_REQUEST會是空呢?我查了些資料,但沒找到原因,以後再找原因吧。

參考資料: