You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PDF转CSV应用前后端交互报错:405与JSON解析错误求助

问题:PDF转CSV应用提交文件时报错排查

点击提交文件时触发报错:An error occurred: SyntaxError: Unexpected end of JSON input,浏览器控制台显示Failed to load resource: the server responded with a status of 405 (Method Not Allowed)。前端(HTML/JS)实际请求5502端口,但后端配置运行在5000端口,导致前后端跨域运行。

后端Python代码

from flask_cors import CORS
import camelot
import pandas as pd
import os

app = Flask(__name__)
app.run(host='127.0.0.1', port=5000)
CORS(app, resources={r"/convert": {"origins": "*"}})


@app.route('/')
def index():
    return render_template('index.html')

@app.route('/convert', methods=['POST'])
def convert():
    print(request.form)
    start_page = int(request.form['start_page'])
    end_page = int(request.form['end_page'])
    filename = request.form['filename']
    output_directory = request.form['output_directory']

    uploaded_file = request.files['pdf_file']  # Get the uploaded PDF file
    pdf_file_path = os.path.join('uploads', uploaded_file.filename)
    uploaded_file.save(pdf_file_path)

    pages_range = f'{start_page}-{end_page}'
    tables = camelot.read_pdf(pdf_file_path, pages=pages_range, flavor='stream')

    dataframes = [table.df for table in tables]
    print("Dataframes successfully created.")
    combined_dataframe_vertical = pd.concat(dataframes, ignore_index=True)

    rows_to_remove = []
    merged_rows = []

    for index, row in combined_dataframe_vertical.iterrows():
        if all(pd.isnull(val) or val == '' for val in row[1:]):
            merged_rows[-1] = merged_rows[-1] + row
            rows_to_remove.append(index)
        else:
            merged_rows.append(row)

    combined_dataframe_vertical.drop(rows_to_remove, inplace=True)
    combined_dataframe_vertical = pd.DataFrame(merged_rows, columns=combined_dataframe_vertical.columns)
    combined_dataframe_vertical.reset_index(drop=True, inplace=True)

    csv_file_path = os.path.join(output_directory, f'{filename}.csv')
    combined_dataframe_vertical.to_csv(csv_file_path, index=False, header=False)

    response_data = {'message': f"Tables extracted and CSV exported to: {csv_file_path}"}

    return jsonify(response_data)  # Return JSON response

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

前端HTML文件

<!DOCTYPE html>
<html>
<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 defer src="/static/pdftocsv.js"></script>
    <title>PDF to CSV Converter</title>
</head>
<body>
    <h1>PDF to CSV Converter</h1>

    <div id="drag-area" style="border: 2px dashed #ccc; padding: 20px; text-align: center;">
        <p>Drag a PDF file here to convert</p>
        <input type="file" id="file-input" name="pdf_file" style="display: none;">
    </div>

    <button id="customize-button" style="display: none;">Convert to CSV</button>
</body>
</html>

前端JS文件

const dragArea = document.getElementById('drag-area');
const fileInput = document.getElementById('file-input');
const customizeButton = document.getElementById('customize-button');
let pdfFilename; // Declare the variable outside of the event handlers

dragArea.addEventListener('dragover', (e) => {
    e.preventDefault();
    dragArea.style.border = '2px dashed #000';
});

dragArea.addEventListener('dragleave', () => {
    dragArea.style.border = '2px dashed #ccc';
});

dragArea.addEventListener('drop', (e) => {
    e.preventDefault();
    const file = e.dataTransfer.files[0];
    fileInput.files = e.dataTransfer.files;
    pdfFilename = file.name; // Set the PDF filename
    customizeButton.style.display = 'block';
    customizeButton.addEventListener('click', () => {
      // Prompt for start page
        const startPage = prompt('Enter Start Page:', '1');
    
        // Prompt for end page
        const endPage = prompt('Enter End Page:', '1');
    
        // Automatically set the output directory to the downloads folder
        const output_directory = 'downloads';
        const defaultCsvFilename = pdfFilename.replace('.pdf', '.csv');
        const customFilename = prompt('Enter File Name:', defaultCsvFilename);
    
        // Send the data to the server for processing
        fetch(window.location.origin + '/convert', {
            method: 'POST',
            body: new URLSearchParams({
                start_page: startPage,
                end_page: endPage,
                output_directory: output_directory,
                filename: customFilename,  // Only send the custom filename
            }),
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }
        })
        .then(response => response.json())
        .then(data => {
            alert(data.message);
        })
        .catch(error => {
            alert('An error occurred: ' + error);
        });
    });
    });

    fileInput.addEventListener('change', () => {
        const file = fileInput.files[0];
        pdfFilename = file.name; // Set the PDF filename
        customizeButton.style.display = 'block';
    });

错误原因及修复方案

1. 跨域请求地址错误

前端用window.location.origin + '/convert'请求的是当前页面所在的5502端口,而非后端的5000端口,导致请求发送到错误的服务器,返回405不允许方法。

修复:
修改JS中的fetch地址为后端实际地址:

fetch('http://127.0.0.1:5000/convert', {
    // ... 其他参数
})

2. Flask初始化顺序错误

后端代码中app.run(host='127.0.0.1', port=5000)在CORS配置和路由定义之前执行,导致:

  • 提前启动了一个未配置CORS和路由的Flask实例
  • 后续的CORS和路由配置不会生效
  • 最后if __name__ == '__main__'又重复启动了一次服务

修复:
删除提前执行的app.run,确保CORS配置在路由之前:

from flask import Flask, request, render_template, jsonify  # 补充缺失的导入
from flask_cors import CORS
import camelot
import pandas as pd
import os

app = Flask(__name__)
CORS(app, resources={r"/convert": {"origins": "*"}})  # CORS配置放在路由前

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/convert', methods=['POST'])
def convert():
    # ... 原有逻辑
    return jsonify(response_data)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)  # 只保留这一处启动代码

3. 未导入Flask核心模块

后端代码缺少request、render_template、jsonify的导入,运行时会抛出NameError,导致接口无法正常响应。

修复:
在开头补充导入:

from flask import Flask, request, render_template, jsonify

4. 前端未发送PDF文件

前端用URLSearchParams只提交了文本参数,没有包含用户上传的PDF文件,后端request.files['pdf_file']会获取不到文件,抛出错误。同时application/x-www-form-urlencoded格式无法传输文件。

修复:
改用FormData来封装文件和所有参数,不需要手动设置Content-Type:

customizeButton.addEventListener('click', () => {
    const startPage = prompt('Enter Start Page:', '1');
    const endPage = prompt('Enter End Page:', '1');
    const output_directory = 'downloads';
    const defaultCsvFilename = pdfFilename.replace('.pdf', '.csv');
    const customFilename = prompt('Enter File Name:', defaultCsvFilename);

    // 创建FormData对象
    const formData = new FormData();
    formData.append('pdf_file', fileInput.files[0]);  // 添加PDF文件
    formData.append('start_page', startPage);
    formData.append('end_page', endPage);
    formData.append('output_directory', output_directory);
    formData.append('filename', customFilename);

    fetch('http://127.0.0.1:5000/convert', {
        method: 'POST',
        body: formData  // 直接传入FormData
    })
    .then(response => response.json())
    .then(data => {
        alert(data.message);
    })
    .catch(error => {
        alert('An error occurred: ' + error);
    });
});

5. 后端缺少目录存在性检查

后端保存文件到uploads目录,但如果该目录不存在,会抛出FileNotFoundError。

修复:
在保存文件前创建目录(如果不存在):

pdf_file_path = os.path.join('uploads', uploaded_file.filename)
os.makedirs('uploads', exist_ok=True)  # 添加这一行
uploaded_file.save(pdf_file_path)

同时确保downloads目录也存在:

csv_file_path = os.path.join(output_directory, f'{filename}.csv')
os.makedirs(output_directory, exist_ok=True)  # 添加这一行
combined_dataframe_vertical.to_csv(csv_file_path, index=False, header=False)

内容的提问来源于stack exchange,提问作者CatLover2291

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 09:57:33