博客 / 詳情

返回

打印FormData、file input只觸發一次change、Blob加File生成文件、FileReader讀取

本文通過標題中的幾個問題功能,提供一些代碼(包括接口,用於複習一下文件操作相關的知識)

問題一 FormData無法直接console.log出來

問題復現

如下代碼:

 <script>
    const formData = new FormData()
    formData.append('name', '孫悟空')
    formData.append('age', 50)
    formData.append('home', '花果山水簾洞')

    console.log('formData-->', formData);
</script>

如下看不到鍵值對的貼圖:

  • 一般來説,我們是使用new FormData()去通過接口,給後端傳遞一些文件信息
  • FormData是無法直接console.log出來的,不過我們可以在請求的載荷中看到
  • 比如以下代碼,筆者提供的一個帶接口的代碼示例(複製粘貼直接看)
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- axios的cdn -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // 生成年月日時分秒
        function time() {
            var today = new Date();
            var y = today.getFullYear();
            var m = today.getMonth();
            var d = today.getDate();
            var h = today.getHours();
            var i = today.getMinutes();
            var s = today.getSeconds();
            m = m + 1;
            d = d < 10 ? "0" + d : d
            m = m < 10 ? "0" + m : m
            i = i < 10 ? "0" + i : i
            s = s < 10 ? "0" + s : s
            return (y + "-" + m + "-" + d + " " + h + ":" + i + ":" + s)
        }
    </script>
</head>

<body>
    <input type="file" />
    <script>
        // 選擇input標籤,並監聽上傳文件change事件
        let ipt = document.querySelector('input') 
        ipt.addEventListener('change', async (e) => {
            // 拿到第一個文件叭
            let file = e.target.files[0]
            const formData = new FormData()
            // 把相關信息丟到formData中
            formData.append('this_is_a_file_for_backend', file)
            formData.append('upload_time', time())
            // 直接打印不出來
            console.log('formData', formData);
            // 執行上傳請求操作
            await uploadFn(formData)
            // e.target.value = null 
        })

        const uploadFn = (params) => {
            return new Promise((resolve, reject) => {
                axios.post('http://ashuai.work/api/simulateUpload', params)
                    .then((res) => {
                        resolve(res.data)
                    })
                    .catch((err) => {
                        reject(err)
                        console.log(err);
                    });
            })
        }
    </script>
</body>
</html>
  • 我們現在在載荷中看一下上傳的參數,如下圖:

FormData類數組循環打印看內容

  • 但是這樣看的話,的確是有些不方便了,不過雖然不能直接打印出來,可以間接去看
  • FormData是一個類數組的東西,所以我們可以循環之,打印看看其每一項的東西內容
  • 如下代碼:
const formData = new FormData()
formData.append('this_is_a_file_for_backend', 'file')
formData.append('upload_time', '2024年1月1日')

// for of 方式
for (const iterator of formData) {
    console.log('for of--->', iterator);
}

// forEach 方式
formData.forEach((value, key) => {
    console.log(key, ' <-----forEach-----> ', value);
})
  • 如下打印圖:

推薦使用Array.from(formData)打印看

  • 不過筆者推薦直接使用Array.from(formData)轉一下,這樣就能夠直接打印了,如下:
const formData = new FormData()
formData.append('this_is_a_file_for_backend', 'file')
formData.append('upload_time', '2024年1月1日')

// 類數組轉一下直接打印的是二維數組
console.log(Array.from(formData));

input的file若還是上一次的文件,則只觸發一次change

  • 當我們選擇文件上傳以後,需要把上一次進行清空
  • 這樣就不會影響繼續上傳操作
  • 如下代碼:
let ipt = document.querySelector('input')
ipt.addEventListener('change', async (e) => {

    // 清空,要不然再上傳同樣的文件就不觸發change事件了
    e.target.value = null 
    // 清空,要不然再上傳同樣的文件就不觸發change事件了
   
    let file = e.target.files[0]
    const formData = new FormData()
    formData.append('this_is_a_file_for_backend', file)
    formData.append('upload_time', time())
    await uploadFn(formData)
})
瀏覽器會把文件進行緩存到變量e.target.value中,若再次觸發上傳操作,會自動比對前一次和這一次文件是否發生變化,若還是上一次的一致,不一致才會觸發change事件

Blob加File生成文本文件

代碼:

/**
 * 使用Blob和File構造函數去創建一個簡單的txt文件
 * */
function createTxtFile(fileName, fileContent) {
    let blob = new Blob([fileContent], { type: 'text/plain' });
    let txtFile = new File([blob], fileName);
    return txtFile
}

let file = createTxtFile('js創建的', 'Hello, World...')
console.log(file)

打印貼圖:

FileReader.readAsText讀取文本文件

  • 模擬需求
  • 假設有一個上傳txt文件的功能,在發送上傳請求前,需要解析一下txt中的內容文字
  • 這個時候,我們就需要去讀取文本文件中的內容,並做一些相關的操作了
  • 即:使用FileReader的readAsText文本讀取方法去操作

代碼

/**
 * 使用FileReader去異步讀取一個簡單的txt文件
 * */
function readTxtFile(file) {
    return new Promise((resolve, reject) => {
        // 實例化文件閲讀器
        var reader = new FileReader();
        // 讀取完成
        reader.onload = function (event) {
            var contents = event.target.result;
            resolve(contents)
        };
        // 讀取錯誤
        reader.onerror = function (err) {
            reject(err)
        };
        // 開始讀取(字符串形式)
        reader.readAsText(file);
        // reader.readAsText(xxx); // 報錯
    })
}

readTxtFile(file).then((res) => {
    console.log('成功', res);
}).catch((err) => {
    console.log('失敗', err);
})

可以結合上述代碼,先生產創建txt文件,再去讀取文件

A good memory is better than a bad pen...

參考資料:

  • FormData: https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
  • Blob:https://developer.mozilla.org/zh-CN/docs/Web/API/blob
  • FileReader:https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
user avatar joyerli 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.