将上传的文件从 JS 发送到 Python CGI 总是会导致字典不够

问题描述 投票:0回答:1

我有一个包含文件输入元素的 HTML 表单。提交表单时,会执行一个 JS 函数来检查上传的文件是否为 PDF 文件且是否小于 10MB。所有这些都可以正常工作,并且正确的文件大小会记录在控制台中。

然后JS函数通过HTTP POST请求将文件发送到Python服务器。但是,发送的文件始终是空对象/字典。当我在发送之前

console.log(file);
时,所有文件数据都会被记录,但是当我查看检查器中的“网络”选项卡时,它只是一个空对象/字典:

但是,当我更改 HTML 表单以便将数据直接发送到 Python CGI 脚本而不先调用 JS 函数时,它工作得很好。

这是我的 HTML 代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="./js/new.js"></script>

    <title>test</title>
  </head>
  <body>
    <script></script>
    <header>
      <h1>File upload test</h1>
    </header>
    <main>
      <fieldset>
        <form
          action="javascript:addFile()"
          id="DataForm"
        >
          <div>
            <label for="file_contract">contract:</label
            ><input
              type="file"
              accept="application/pdf"
              id="file_contract"
              name="file_contract"
            />
          </div>
          <button type="button" class="outlined" onclick="cancel()">
            Annuleer
          </button>

          <input type="submit" value="Bewaar" />
        </form>
      </fieldset>
    </main>
    <script>
      function cancel() {
        const host = window.location.host;
        const redirectUrl = `http://${host}`;
        window.location.replace(redirectUrl);
      }
    </script>
  </body>
</html>

JavaScript 代码:

async function addFile() {
  const formData = new FormData(document.forms["DataForm"]);
  // get uploaded files
  // Function to check and append a file to the formData
  function handleFileUpload(fileInputId) {
    console.log("uploading file...");
    const fileInput = document.getElementById(fileInputId);
    const file = fileInput.files[0];

    // Check if a file is selected

    // Ensure the file is a PDF
    if (file.type === "application/pdf") {
      console.log(`filesize = ${file.size}`);
      // 10MB in bytes
      formData.append(fileInputId, fileInput.files[0]);
    }

    return true;
  }

  // Handle each file upload separately
  if (!handleFileUpload("file_contract")) {
    console.log("form submission prevented");
    return false; // Prevent form submission
  }

  const data = {
    answers: Object.fromEntries(formData),
  };

  console.log(data["answers"]["file_contract"]);

  try {
    const response = await fetch("../../cgi-bin/addRecord.py", {
      method: "POST",
      headers: {
        "Content-type": "application/json; charset=UTF-8",
      },
      body: JSON.stringify(data),
    });

    const responseData = await response.json();

    if (responseData.hasOwnProperty("status")) {
      if (responseData["status"] === "failed") {
        // session is invalid / expired
        alert("Login sessie vervallen. Log opnieuw in.");
      } else if (responseData.status === "success") {
        const host = window.location.host;
        const redirectUrl = `http://${host}`;
        console.log("redirecting... (commented out)");
        // window.location.replace(redirectUrl);
      } else {
        alert(
          "Fout bij het opslaan. Probeer het nog eens of contacteer Sandra."
        );
      }
    }
  } catch (error) {
    console.error(error);
    alert("Fout bij het opslaan. Probeer het nog eens of contacteer Sandra.");
  }
}

Python CGI:

#!/usr/bin/env python3

import cgi
import hashlib
import json
import os
import sys
import logging

# Get the absolute path of the current script
dirname = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

if os.path.exists(f'{dirname + "/log.log"}'):
    # If it exists, open it in write mode to clear its contents
    with open(f'{dirname + "/log.log"}', 'w'):
        pass

logging.basicConfig(filename=f'{dirname + "/log.log"}', level=logging.DEBUG)


def generate_response(status, data=None):
    response = {"status": status}
    if data:
        response["data"] = data

    print("Content-Type: application/json")
    print()
    print(json.dumps(response))


def calculate_file_hash(file_data):
    # Create an MD5 hash object
    md5_hash = hashlib.md5()

    # Read the file data in chunks to efficiently handle large files
    for chunk in iter(lambda: file_data.read(4096), b''):
        md5_hash.update(chunk)

    # Return the hexadecimal representation of the MD5 hash
    return md5_hash.hexdigest()


def main():
    # read form data from HTTP POST request
    data = sys.stdin.read(int(os.environ.get('CONTENT_LENGTH', 0)))
    post_data = json.loads(data)

    answers = post_data["answers"]

    logging.debug(str(answers))

    if "file_contract" in answers:
        contract_file = answers['file_contract']
        contract_file_filename = contract_file.filename
        contract_file_hash = calculate_file_hash(contract_file.file)
        # save the file data to the werkmap
        # Save the PDF file to a folder
        contract_filename_path = os.path.join(dirname, "documenten", contract_file_hash)
        with open(contract_filename_path, 'wb') as contract_file_handle:
            contract_file_handle.write(contract_file.file.read())

    generate_response("success")


if __name__ == "__main__":
    main()

javascript python file upload cgi
1个回答
0
投票

问题是

JSON.stringify
- 它无法将
File
对象转换为
JSON

如果您确实必须将其作为 JSON 发送,那么您可能需要使用

FileReader
和函数
readAsDataUri
将文件内容转换为
Base64

最小工作示例。

async function addFile() {
  const formData = new FormData(document.forms["DataForm"]);
  const file = formData.get('file_contract');
  
  const reader = new FileReader();
  
  reader.onload = () => {
      //const data = {'answers': reader.result}  // send only file contemt
      const data = {'answers': 
                     {
                       'data': reader.result,  // file content as `Base64`
                       'name': file.name, 
                       'size': file.size, 
                       'type': file.type,
                       'lastModified': file.lastModified,
                     }
                   }
      
      fetch("/addRecord", {
        method: "POST",
        headers: {
           //'Accept': 'application/json, text/plain, */*',   // expect JSON or TEXT as response from server
           'Content-Type': 'application/json; charset=UTF-8'  // inform server that you send JSON
        },        
        body: JSON.stringify(data),  
      }).then((response) => {
          if(response.ok) {
            const responseData = response.json();
            //TODO: use responseData 
          }
      });
  }  
  
  reader.readAsDataURL(file);  // convert to `Base64` and execute `onload()`
}
© www.soinside.com 2019 - 2024. All rights reserved.