整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Javascript基于模板生成PDF 文檔

          您的 js 應用程序中使用 eDocGen 從 JSON/XML/Database 創建 PDF 文檔的指南。

          文檔生成是開發人員生活中非常普遍的需求。無論是電子商務網站、管理應用程序還是其他任何東西。它可以是發票生成、保險文件準備、醫生處方、人力資源報價生成、工資單生成,你可以想到大量的用例。總是需要生成文檔。

          從開發人員的角度來看,有幾種常見的方法可以完成這項工作。

          1. 創建 HTML 元素并打印它們以生成文檔
          2. 使用一些庫來生成文檔
          3. 讓服務器處理基于靜態模板的文檔生成

          這些方法對我沒有幫助??蛻粝M约憾ㄖ扑麄兊奈募?。我一直在尋找一種方法,發現eDocGen是一種單點解決方案。

          與其他服務不同,eDocGen 提供了可以集成到我們應用程序中的 RestAPI。

          在本文中,我們將討論如何將 eDocGen 集成到我們的 js 應用程序中,以從各種數據格式(如 JSON/XML/Database 模式)生成文檔。請免費試用以開始編碼。

          讓我們潛入并編寫代碼。

          項目設置

          出于演示目的,我創建了一個在 nodejs 上運行的示例 js 應用程序。

          請按照以下步驟為我們設置編碼游樂場。

          步驟1:

          用于npm init創建 package.json

          第2步:

          添加axios, form-data, request,xhr2開發此應用程序所需的依賴項npm install axios form-data request xhr2

          第 3 步:

          我們需要一個索引文件作為我們應用程序的起點。在根目錄中創建一個 index.js 文件并修改 package.json 如下所示。

          JSON

          scripts": {
              "start": "node index.js"
            }

          現在我們有一個基本的應用程序可以開始。這些步驟結束后,package.json 應該如下所示。

          JSON

          {
            "name": "nodejs-multiple-upload-files",
            "version": "1.0.0",
            "main": "index.js",
            "scripts": {
              "start": "node index.js"
            },
            "dependencies": {
              "axios": "^0.27.2",
              "form-data": "^4.0.0",
              "request": "^2.88.2",
              "xhr2": "^0.2.1"
            }
          }
          

          登錄

          雖然這篇文章是關于文檔生成的,但我們需要登錄才能獲取我們的訪問令牌。這是一個典型的JWT令牌,將用于授權文檔生成 API。

          JavaScript

          var XMLHttpRequest = require("xhr2");
          var xhr = new XMLHttpRequest();
          
          module.exports.getToken = function (callback) {
            var data = JSON.stringify({
              username: "<your username>",
              password: "<password>",
            });
          
            xhr.addEventListener("readystatechange", function () {
              if (this.readyState === 4) {
                token = JSON.parse(this.responseText).token;
          
                console.log("User Token", token);
                callback(token);
              }
            });
          
            xhr.open("POST", "https://app.edocgen.com/login");
            xhr.setRequestHeader("content-type", "application/json");
            xhr.setRequestHeader("cache-control", "no-cache");
            xhr.send(data);
          };
          

          我們可以將令牌在應用程序中緩存一個小于過期時間的時間段,并使用它來生成文檔或上傳模板。到期時間過后,我們可以刷新令牌。緩存可以是 Redis 或內存緩存。這取決于您的應用程序設計。

          模板設計

          如上所述,eDocGen 允許用戶自定義和上傳模板。但是如何動態映射數據呢?有一些將數據映射到文檔的規則。我們將看到如何使用規則創建模板。

          看看這個文件。

          eDocGen{}對動態字段使用由 括起來的標簽。我們可以動態添加文字、logo、表格、條件語句、數學計算等。

          例如,在上圖中,

          字符串字段: {Invoice_Number}{Invoice_Date}配置為替換為模板中的文本。模板中 {} 內的任何內容都將與輸入數據匹配并替換。

          動態表: 當表中存在需要循環和替換的數據數組時,動態表將是一個不錯的選擇。表中的行以 開頭{#tablename}和結尾{/tablename}。在上面的示例中,發票表中的一行在第一列以 {#IT} 開頭,在最后一列以 {/IT} 結尾。行中的列可以有字符串字段。在我們的示例中,{Item_description}并且{Amount}

          圖片: eDocGen 提供動態添加圖片到模板的功能。請按照以下步驟操作。

          • 將圖像上傳到應以 image_id 響應的 eDogGen。
          • {%image_id}是用于填充圖像的標簽。圖像image_id將從 eDocGen 存儲中獲取并替換為{%image_id}. 預計image_id將出現在輸入數據中。

          基于條件的動態字段(If-Else):可以使用條件標簽有條件地顯示內容。例如,當語言為英語時,文檔中會顯示{#language == "english"} 英語內容。同樣,單個文檔模板可以支持多種語言。

          數學計算: eDocGen 支持基于模板中定義的公式的數學計算。可以使用以下公式計算發票中項目金額的總和。

          JSON

          {
              IT // array of items
              | summation:'Amount' // value that needs to be used for calculation 
              | format_number: ",” // format of the value
          }

          請前往JSON-to-pdf了解更多詳情。

          模板上傳

          準備好模板后,就可以將其上傳以供使用。有兩種方法。

          1. eDocGen 的交互式 UI - 與 Dropbox、驅動器、Evernote 集成
          2. eDocGen 的 RestAPI - 可以集成到客戶端代碼中以上傳模板。

          對于演示,我使用 UI 來上傳模板。成功上傳后,我們會得到一個 ID 作為響應。這是將用于生成文檔的 ID。

          如果您希望使用 API,請在此處留下 Upload API 結構供您參考。

          JSON

          
          "/api/v1/document": {
            "post": {
              "tags": [
                "Document"
              ],
              "description": "Upload template to eDocGen",
              "produces": [
                "application/json"
              ],
              "consumes": [
                "multipart/form-data"
              ],
              "parameters": [
                {
                  "name": "documentFile",
                  "description": "file to upload",
                  "required": true,
                  "type": "file",
                  "in": "formData"
                },
                {
                  "name": "x-access-token",
                  "in": "header",
                  "description": "JWT auth token from login",
                  "required": true,
                  "type": "string"
                }
              ],
              "responses": {
                "200": {
                  "description": "Successfully uploaded document file"
                },
                "other": {
                  "description": "Operation failed"
                }
              }
            }
          }

          JSON 到文檔生成

          現在我們準備好了模板。讓我們生成文檔。

          文檔生成有兩個階段。

          1. 請求生成文檔
          2. 下載文件

          第 1 步:請求生成文檔

          我們要求生成包含所需詳細信息的文檔,并得到確認。該過程異步發生在屏幕后面。

          文檔生成所需的參數

          應用程序接口:POST-/api/v1/document/generate/bulk

          請求正文

          表格數據

          文檔 ID

          模板的id

          格式

          pdf/docx(模板應支持格式)

          輸出文件名

          輸出文件的文件名。

          輸入文件

          該文件包含標記值。支持 json、xlsx 和 xml。

          標題

          內容類型

          多部分/表單數據

          x-訪問令牌

          來自登錄的 JWT 身份驗證令牌

          輸入數據

          inputFile 中的數據應該是模板定義的結構。例如,對于上面的模板映射將如下所示。

          • Invoice_Number在 JSON 中應該與{Invoice_Number}模板中的匹配。
          • 對于表數據,它應該是一個對象數組,帶有Item_DescriptionAmount.
          • 金額應該是一個用于求和計算的數字。

          第 2 步:下載文件

          可以使用從上述步驟中獲得的輸出 ID 和輸出文件的名稱下載生成的文檔。

          我們將在這里使用兩個 API。

          1. 了解文件存在的 API:/api/v1/output/name/${fileName}
          2. 下載文件的API:/api/v1/output/download/${outputId}

          由于文檔生成是異步發生的,要知道文檔是否生成,我們將使用/api/v1/output/nameapi。

          來自 API 的成功響應/api/v1/output/name將下載文件。

          我將這兩個步驟組合在一個 js 文件中,如下所示。

          爪哇

          let login = require("../edocgen_login");
          const fs = require("fs");
          const uuid = require("uuid");
          const FormData = require("form-data");
          let axios = require("axios");
          let fileName = uuid.v4();
          const headers = {
            "Content-Type": "multipart/form-data",
            "x-access-token": "null",
          };
          
          const hostName = "https://app.edocgen.com/api/v1/document/generate/bulk";
          
          
          const outputFormat = "<format>";// pdf / docx
          const documentId = "<template_id>";    // id of the template we want to use
          
          module.exports.generateFiles =  function () {
            let authToken = login.getToken(function handleUsersList(token) {
          
              headers["x-access-token"] = token;
          
              var formBody = new FormData();
              formBody.append("documentId", documentId);
              formBody.append("format", outputFormat);
              formBody.append("outputFileName", fileName);
              // json data for the template
              formBody.append("inputFile", fs.createReadStream("./JSON_Data_Single.json"));   // local path forjson file
          
              let config = {
                method: "post",
                url: hostName,
                headers: headers,
                data: formBody,
              };
          
              console.log(`https://app.edocgen.com/api/v1/output/name/${fileName}.${outputFormat}`);
              let config_output = {
                method: "get",
                url:`https://app.edocgen.com/api/v1/output/name/${fileName}.${outputFormat}`,
                headers: headers,
              };
          
              const MAX_RETRY = 50;
              let currentRetry = 0;
          
              // max retry for 50 times
              function errorHandler() {
                if (currentRetry < MAX_RETRY) {
                  currentRetry++;
                  console.log("Document is not prepared yet! Retrying...");
                  sendWithRetry(processResponse);
                } else {
                  console.log("No luck. Document is not generated. Retried multiple times.");
                }
              }
              
              // sendWithRetry checks for file existence
              // on success, it proceeds to download the file
              // on failure, it retries 
              // todo: introduce spin lock
              function sendWithRetry(callback) {
                axios(config_output)
                  .then(function (response) {
                    if (response.data.output.length !== 1) {
                      throw new axios.Cancel("Document is not found. Throw error.");
                    } else {
                      callback(response);
                    }
                  })
                  .catch(errorHandler);
              }
          
              axios(config)
                .then(function (response) {
                  sendWithRetry(processResponse);
                })
                .catch(function (error) {
                  console.log(error);
                });
            });
          };
          
          function processResponse(response) {
            const outputId = response.data.output[0]._id;
            console.log(
              "Output Document is Generated. Id = ",
              response.data.output[0]._id
            );
          
            let config_download = {
              method: "get",
              url: `https://app.edocgen.com/api/v1/output/download/${outputId}`,
              headers: headers,
              responseType: "arraybuffer",
          
            };
          
            axios(config_download)
              .then(function (response) {
                console.log("Output file is downloaded " + `${fileName}.${outputFormat}`);
                fs.writeFileSync(`./${fileName}.${outputFormat}`, response.data);
              })
              .catch(function (error) {
                console.log("Error while downloading");
                console.log(error);
              });
          }
          

          單個與多個文檔

          當數據為單個 JSON 時,將生成給定格式的單個文檔。

          當數據是對象數組時,將生成每個數組元素的文檔并將其壓縮到文件中。

          XML 到文檔生成

          XML 數據的過程很簡單。我們需要做的就是傳遞 XML 文件來代替 JSON 數據。

          就像JSON to document,XML to Document 我們也需要documentId, outputFileName, format and inputFile。除輸入文件外,與 JSON 相同的所有內容都將是 XML 文件。

          示例 XML 數據如下所示

          XML

          <?xml version="1.0" encoding="UTF-8" ?>
          <marker>
            <values>
              <Invoice_Number>SBU-2053501</Invoice_Number>
              <Invoice_Date>31-07-2020</Invoice_Date>
              <Terms_Payment>Net 15</Terms_Payment>
              <Company_Name>ABC company</Company_Name>
              <Billing_Contact>ABC-Contact1</Billing_Contact>
              <Address>New york, United State</Address>
              <Email>support@edocgen.com</Email>
          	<Logo>621cd2b783a6095d7b15a443</Logo> 
               <Sum1>6,751</Sum1>
          	 <para>61b334ee7c00363e11da3439</para>
              <ITH>
                <Heading1>Item Description</Heading1>
                <Heading2>Amount</Heading2>
              </ITH>
              <IT>
                <Item_Description>Product Fees: X</Item_Description>
                <Amount>5,000</Amount>
              </IT>
            </values>
          <marker>

          我為 XML 作為數據源所做的代碼更改很簡單,如下所示

          JavaScript

          var formBody = new FormData();
          formBody.append("documentId", documentId);
          formBody.append("format", outputFormat);
          formBody.append("outputFileName", fileName);
          formBody.append("inputFile", fs.createReadStream("./XML_Invoice.xml"));

          數據庫到文檔生成

          從數據庫生成文檔幾乎與其他數據源相同。但在這種情況下,我們需要提供連接詳細信息和 SQL 查詢,而不是上傳 inputFile。

          SQL 查詢的輸出列應與文檔模板中的標簽匹配。

          讓我們看看如何在代碼中進行配置。

          JavaScript

          const templateId = "<template id>";
          const dbVendor = "mysql";
          const dbUrl = "<jdbc connection URL>";
          const dbLimit = "100";
          const dbPassword = "<database password>";
          const dbQuery = "SELECT JSON_ARRAY(first, last) FROM customers;";
          const outputFormat = "pdf";
          
          // form data prepareation
          let formBody = new FormData();
          formBody.append("documentId", templateId);
          formBody.append("format", outputFormat);
          formBody.append("dbVendor", dbVendor);
          formBody.append("dbUrl", dbUrl);
          formBody.append("dbLimit", dbLimit);
          formBody.append("dbPassword", dbPassword);
          formBody.append("dbQuery", dbQuery);
          formBody.append("outputFileName", fileName);

          其他一切都將保持不變。

          通過電子郵件發送文檔

          eDocGen 提供了通過電子郵件發送生成的文檔的功能。

          文檔生成所需的參數

          應用程序接口:POST-/api/v1/output/email

          請求正文

          JSON

          出局

          將需要通過電子郵件發送的輸出 ID 放在這里

          電子郵件ID

          將用戶電子郵件放在這里

          標題

          內容類型

          多部分/表單數據

          x-訪問令牌

          來自登錄的 JWT 身份驗證令牌

          代碼示例

          let login = require("../edocgen_login");
          let axios = require("axios");
          const hostName = "https://app.edocgen.com/api/v1/output/email";
          const headers = {
            "Content-Type": "application/json",
            "x-access-token": "null",
          };
          
          const outId = "<output ID>"; // Put output ID here which need to be sent via email
          const emailId = "<user email>"; // Put user email here
          
          module.exports.generateFiles = function () {
            let authToken = login.getToken(function handleUsersList(token) {
              headers["x-access-token"] = token;
          
              let payload = { outId: outId, emailId: emailId };
              let config = {
                method: "post",
                url: hostName,
                headers: headers,
                data: payload,
              };
          
              axios(config)
                .then(function (response) {
                  console.log("Mail sent");
                })
                .catch(function (error) {
                  console.log(error);
                });
            });
          };
          

          來自 eDocGen 的電子郵件如下所示

          還有很多其他的功能我在這里無法涵蓋。但我希望這篇文章可以為您提供一個從哪里開始的想法。

          前一陣兒被某網站的 JS 反爬流程難住了,至今也沒明白它的反扒原理和攻破方法。最終找到了一個自動化腳本工具 autoit 3,用一個笨方法將人手動點擊瀏覽器的動作腳本化,達到網頁數據獲取目的,拿到網頁文件后,再用代碼解析,曲線完成任務。

          本文將介紹這個自動化的過程,并帶編寫一個完整的 autoit 3 爬蟲腳本,希望對各位讀者朋友有所啟發。

          自動化操作分析

          以國家信息安全漏洞共享平臺為例,它在返回數據前發起了兩次 512 響應,第三次瀏覽器帶著動態生成的 Cookie 信息才能得到數據。

          這次咱們直接從網頁入手,操作鍵盤找到“下一頁” 按鈕,按下 Enter 鍵完全請求。通過鍵盤定位到 “下頁” 按鈕的過程為:

          1. 第一步,按下 “End” 鍵,到達網頁底部;
          2. 第二步,反向 “Tab” 鍵,按 15 次就可定位到 “下頁” 按鈕。

          接著就可以編寫自動化腳本了,把剛剛的手動操作翻譯成腳本命令:

          1. 切換為英文輸入法,保證瀏覽器輸入欄信息正確;
          2. 打開 Chrome 瀏覽器;
          3. 向瀏覽器地址欄輸入目標 URL;
          4. 按下 Enter 鍵,等待 2 秒保證頁面數據加載完成;
          5. 按下 Ctrl +S 鍵,并向存儲路徑發送存儲文件名稱,等待“保存”操作完成;
          6. 按下 End 鍵盤,定位到頁面底部;
          7. 按下反向 Tab 鍵 15 次,定位到 “下頁” 按鈕;
          8. 按下 Enter 鍵,請求下一頁數據;
          9. 循環 5-8 這個過程 N 次,N=需要爬的頁數。

          這個流程,對其他高反扒的信息發布網站,也是適用的。

          編寫自動化腳本

          按照上面的流程,編寫 autoit 自動化腳本,創建一個 myspider.au3 文件:

          #include <AutoItConstants.au3>
          
          ;;切換為英文輸入法,保證瀏覽器輸入正常
          $hWnd = WinGetHandle("[ACTIVE]");$hWnd 為目標窗口句柄,這里設置的是當前活動窗口
          $ret = DllCall("user32.dll", "long", "LoadKeyboardLayout", "str", "08040804", "int", 1 + 0)
          DllCall("user32.dll", "ptr", "SendMessage", "hwnd", $hWnd, "int", 0x50, "int", 1, "int", $ret[0])
          
          $url = "https://www.cnvd.org.cn/flaw/list.htm"
          spiderData($url)
          
          Func spiderData($url)
          	;;打開 Chrome 瀏覽器窗口
          	$chromePath = "C:\Users\admin\AppData\Local\Google\Chrome\Application\chrome.exe"
          	Run($chromePath)
          
          	;;登錄窗口顯示
          	WinWaitActive("[CLASS:Chrome_WidgetWin_1]")
          	;; 休息2000毫秒
          	Sleep(2000)
          	;; 移動窗口
          	WinMove("[CLASS:Chrome_WidgetWin_1]", "打開新的標簽頁 - Google Chrome", 0, 0,1200,740,2)
          
          	;; 休息500毫秒
          	Sleep(500)
          
          	;;地址欄輸入URL 并按下 Enter 鍵
          	Send($url)
          	Sleep(500)
          	Send("{enter}")
          	Sleep(3000)
          
          	;; 循環爬取需要的頁數,測試只爬3頁
          	For $i = 1 To 3 Step 1
          		;;打開右鍵另存為按鈕: Ctrl+S
          		send("^s")
          		Sleep(2000)
          		WinWait("[CLASS:#32770]","",10)
          
          		;;將存儲路徑設置到另存為組件輸入框 Edit1 里
          		$timeNow = @YEAR & "" & @MON & "" & @MDAY & "" & @HOUR & "" & @MIN
          		$savePath = "F:\A2021Study\ListData\" &$timeNow &  "_page" & $i & ".html"
          		ControlSetText("另存為","", "Edit1", $savePath)
          
          		;;點擊確定
          		ControlClick("另存為","","Button2")
          
          		;;再次確定
          		WinWait("[CLASS:#32770]","",10)
          		ControlClick("確認另存為","","Button1")
          
          		;; 等待保存操作完成
          		Sleep(3000)
          
          		;; 定位到下一頁按鈕,并觸發點擊下一頁
          		send("{END}")
          		Send("+{TAB 15}")
          		Send("{enter}")
          
          		;;點擊確定后,等待網頁加載完成
          		Sleep(3000)
          	Next
          
          	;; 整個操作完成,則關閉瀏覽器
          	Send("^w")
          EndFunc

          腳本編寫過程中,有幾點需要注意:

          • 第一,輸入法切換很重要,否則 URL 地址欄的值很容易亂;
          • 第二, windows 的文件路徑是反斜杠 \ ,否則會導致另存為的路徑無法識別;
          • 第三,幫助文檔里面提供的關閉方法是 WinClose ,但是反復測試,確定這個方法不靠譜,要么會引起瀏覽器異常關閉導致下次打開會恢復上次的網址;要么完全不生效。迂回的解決辦法是用關閉按鍵 Ctrl+W ,完成了正常關閉的目的。

          因為爬蟲要作為定時任務運行的,為避免打開太多瀏覽器窗口,因此需要在腳本結束時關閉瀏覽器。

          啟示錄

          數據爬取一般分為列表頁和詳情頁,定位點擊每一條詳情的過程比較麻煩,所以爬取詳情頁面的和列表分開,用 Java 代碼解析所有詳情 URL 后,再由另一個 autoit 腳本去獲取詳情頁面,這個流程大家可以自己寫一下,這里就不詳細介紹了。

          最后再匯總下整個爬取的流程:

          第一步,執行爬取列表的 autoit 腳本,得到列表頁面 html;
          第二步,解析列表頁 html ,得到所有詳情頁面的 URL ,寫入到文件中;
          第三步,執行爬取詳情頁面的 autoit 腳本,它遍歷第二步的目標 URL ,得到詳情頁 html ;
          第四步,解析詳情頁 html 文件,得到詳情數據。
          

          總控流程、第二步和第四步的解析都用 Java 代碼完成,用 Runtime.getRuntime().exec("cmd /c E:\A2021Study\Autoit3\myspider.au3") 調用腳本,文件路徑是反斜杠。

          這個方法雖然有點笨,但完全是人工操作瀏覽器,能夠對抗反爬蟲策略,感興趣的朋友可以執行下本文的腳本試試。

          autoit 還是蠻有意思的,語法也很簡單,DirCreate 創建文件,iniread 讀取配置項,一行代碼頂 Java 幾十行,不得不承認 Java 操作文件才是最麻煩的哇!

          :如何用 JS 一次獲取 HTML 表單的所有字段 ?

          考慮一個簡單的 HTML 表單,用于將任務保存在待辦事項列表中:

            <form>
              <label for="name">用戶名</label>
              <input type="text" id="name" name="name" required>
          
              <label for="description">簡介</label>
              <input type="text" id="description" name="description" required>
          
              <label for="task">任務</label>
              <textarea id="task" name="task" required></textarea>
          
              <button type="submit">提交</button>
            </form>
          

          上面每個字段都有對應的的type,ID和 name屬性,以及相關聯的label。用戶單擊“提交”按鈕后,我們如何從此表單中獲取所有數據?

          有兩種方法:一種是用黑科技,另一種是更清潔,也是最常用的方法。為了演示這種方法,我們先創建form.js,并引入文件中。

          從事件 target 獲取表單字段

          首先,我們在表單上為Submit事件注冊一個事件偵聽器,以停止默認行為(它們將數據發送到后端)。

          然后,使用this.elements或event.target.elements訪問表單字段:

          相反,如果需要響應某些用戶交互而動態添加更多字段,那么我們需要使用FormData。

          使用 FormData

          首先,我們在表單上為submit事件注冊一個事件偵聽器,以停止默認行為。接著,我們從表單構建一個FormData對象:

          const form = document.forms[0];
          
          form.addEventListener("submit", function(event) {
            event.preventDefault();
            const formData = new FormData(this);
          });
          

          除了append()、delete()、get()、set()之外,FormData 還實現了Symbol.iterator。這意味著它可以用for...of 遍歷:

          const form = document.forms[0];
          
          form.addEventListener("submit", function(event) {
            event.preventDefault();
            const formData = new FormData(this);
          
            for (const formElement of formData) {
              console.log(formElement);
            }
          })
          

          除了上述方法之外,entries()方法獲取表單對象形式:

          const form = document.forms[0];
          
          form.addEventListener("submit", function(event) {
            event.preventDefault();
            const formData = new FormData(this);
            const entries = formData.entries();
            const data = Object.fromEntries(entries);
          });
          

          這也適合Object.fromEntries() (ECMAScript 2019)

          為什么這有用?如下所示:

          const form = document.forms[0];
          
          form.addEventListener("submit", function(event) {
            event.preventDefault();
            const formData = new FormData(this);
            const entries = formData.entries();
            const data = Object.fromEntries(entries);
          
            // send out to a REST API
            fetch("https://some.endpoint.dev", {
              method: "POST",
              body: JSON.stringify(data),
              headers: {
                "Content-Type": "application/json"
              }
            })
              .then(/**/)
              .catch(/**/);
          });
          

          一旦有了對象,就可以使用fetch發送有效負載。

          小心:如果在表單字段上省略name屬性,那么在FormData對象中剛沒有生成。

          總結

          要從HTML表單中獲取所有字段,可以使用:

          • this.elements或event.target.elements,只有在預先知道所有字段并且它們保持穩定的情況下,才能使用。

          使用FormData構建具有所有字段的對象,之后可以轉換,更新或將其發送到遠程API。*


          作者:VALENTINO GAGLIARDI 譯者:前端小智 來源:valentinog

          原文:https://www.valentinog.com/blog/form-data/


          主站蜘蛛池模板: 国产未成女一区二区三区| 女人18毛片a级毛片一区二区| 视频一区精品自拍| 免费看AV毛片一区二区三区| 国内精品视频一区二区三区| 免费无码VA一区二区三区| 亚洲一区二区三区在线网站| 精品视频在线观看你懂的一区| 久99精品视频在线观看婷亚洲片国产一区一级在线| 偷拍精品视频一区二区三区| 麻豆精品一区二区综合av| 色欲AV蜜桃一区二区三| 另类ts人妖一区二区三区| 国产成人免费一区二区三区| 久久久国产精品无码一区二区三区 | 韩国精品一区视频在线播放| 无码aⅴ精品一区二区三区| 国产乱码精品一区二区三区香蕉 | 日韩国产精品无码一区二区三区| 国产美女视频一区| 国产精品合集一区二区三区 | 久久精品岛国av一区二区无码| 果冻传媒一区二区天美传媒| 久久免费视频一区| 极品尤物一区二区三区| 国精产品999一区二区三区有限| 午夜精品一区二区三区在线视| 国产在线视频一区二区三区| 久久影院亚洲一区| 亚洲国产高清在线精品一区| 日本亚洲成高清一区二区三区| 亚洲爽爽一区二区三区| 人妻无码一区二区三区| 精品视频在线观看你懂的一区 | 精品视频在线观看一区二区 | 日韩内射美女人妻一区二区三区 | 亚洲国产专区一区| 亚洲线精品一区二区三区影音先锋 | 一级特黄性色生活片一区二区| 欧美av色香蕉一区二区蜜桃小说| 国产一区在线电影|