DEV Community

Mesak
Mesak

Posted on • Edited on • Originally published at mesak.tw

傳送 物件/陣列 到 FORMDATA

JS 自從有了打包工具之後,很多工具造福不少開發者,以往要送個 AJAX 到後端,大多都是一個頁面處理一個 method

框架化後,每個程式碼開始有了 物件化、封裝方式來做,現在大多都用 axios 來送 AJAX

但是遇到一個問題就是送檔案的時候,需要使用到 FormData 的物類別物件。

但是整個像後端送的程式碼都封裝起來了,要針對一個檔案上傳的方法,做出額外處理,當檔案上傳處理頁面多了之後維護除錯方法就有點麻煩。

所以比較好的方式還是修改 API 送出前,先把物件轉換好判定是否有 File 類型,然後轉換成 FormData 就可以一勞永逸

問題來了,以往直接用 axios 傳送到後端,一個 object 就可以搞定 value is array 的問題,開發者似乎不用煩惱太多後續處理問題

頂多就是遇到 GET 網址參數需要轉換純文字的狀態,但也也只需要一個 qs 套件就可以解決解決

但如果採用 FormData 這個類型的時候,整個 object 就必須自己處理,如果遇到傳送一個陣列的時候,就必須額外針對 key = array 的狀態去處理,然後如果 value = array 也是很麻煩的事情。

舉個例子,往後端送 array1 = [1,2,3] ,如果是 axios 就可以直接送,如果是送 FormData 就必須處理成

array1[]:1;
array1[]:2;
array1[]:3;
Enter fullscreen mode Exit fullscreen mode

這點可以利用 PostMan 來做測試看看後端可以吃到怎樣結構的陣列

這邊原本的想法是,把物件的每個 value 歷遍,只要 value instanceof File 就成立條件,把 key 轉換成陣列

不過找了很多遞迴解法,都沒有 qs 轉換 陣列來的快速有效,最終還是爬了一下 qs 的實際操作用法,

  const files = new Map()
  const queryString = qs.stringify(data, {
    arrayFormat: 'brackets',
    encode: false,
    filter: (name, value) => {
      if (value instanceof File) {
        let id = lodash.uniqueId('__FILE__.')
        files.set(id, value)
        return id
      } else {
        return value
      }
    }
  })
Enter fullscreen mode Exit fullscreen mode

利用 qs 的 filter method 把所有 value 檔案移到暫存變數 files 裡面,利用 lodash 取得目前唯一的數值,將原先的 data (object) 的 File 類型 value,全部轉換成 file 的 uniqueId,最終就可以得到一串處理過的字串,快速的將 物件陣列轉換成字串

a=__FILE__.1&b[a]=1&b[b]=2 ...
Enter fullscreen mode Exit fullscreen mode

只要有了這個純字串的東西,要拆解成陣列 + 物件就方便多拉,拆完之後遇到 FILE 的 value 記得去把 files 裡面的檔案給領回來,這樣塞入 FormData 就可以解決陣列 key 或是 value = array 的問題了

完整範例放在 codepen.io

Top comments (0)