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

AWS部署的React+Express应用文件上传POST请求无响应求助

问题描述

我在AWS部署了一套React+Express生产环境应用,整体运行基本正常,但向Express服务器发起文件上传POST请求时无任何响应,本地环境下该功能可正常上传文件并推送至API。同时Nginx出现错误:Upstream prematurely closed connection while reading response header from upstream。以下是相关代码配置,恳请提供解决建议:


server.js

const express = require('express');
const path = require('path');
const docparser = require('docparser-node');
const client = new docparser.Client("APIKEY");
const parserID = "cslfmvewjrvo";
const multer = require('multer');

const app = express();
//this creates the filestream needed to upload to API endpoint
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'client/public/uploads')
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname)
  }
})

const upload = multer({ storage });

const fs = require('fs')

// Upload Route
app.post('/api/upload', upload.single('file'), async (req, res) => {
  const file = req.file;
  const stream = fs.createReadStream(`${__dirname}/../client/public/uploads/${file.filename}`);

  if (file == null) {
    return res.status(400).json({ msg: 'No file uploaded' });
  }
  try 
  {
    let result = await client.uploadFileByStream(parserID, stream);
    let docID = result.id;
    console.log("This is the document ID: " + docID);
    console.log("waiting for response...");

    //We have to wait for a response from the API so thats what delay is for
    await delay(20000);
    let parsedData = await client.getResultsByDocument(parserID,docID, {format: 'object'});
    let transcript = parsedData[0].final;  

    return res.status(200).send(transcript); 
  } 
  catch (err) 
  {
    console.error(err)
    return res.status(500).json({ msg: 'An error occurred' })
  }
});

// This function delays the actions of whatever code precedes it
function delay(time) {
  return new Promise(resolve => setTimeout(resolve, time));
};

app.listen(5000, () => console.log('Server Started...'));

FileUpload.js

//FileUpload.js

import React, { Fragment, useState, useEffect,useMemo } from 'react';
import axios from 'axios';
import Table from "./Table";
import Message from './Message';
import Progress from './Progress';
import '../App.css';
//This is the fileUploader Component. 
 
const FileUpload = () => {
  const [file,setFile] = useState('');
  const [fileName,setFileName] = useState('Choose File');
  const [data,setData] = useState([]);
  const [uploadedFile, setUploadedFile] = useState({});
  const [message, setMessage] = useState('');
  const [uploadPercentage, setUploadPercentage] = useState(0);
  const [tableData,setTableData] = useState(true);
 
const onChange = e => {
  setFile(e.target.files[0]);
  setFileName(e.target.files[0].name);
  };

  const onSubmit = async e => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('file', file);

    try {
      const res = await axios.post('/api/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        onUploadProgress: progressEvent => {
          setUploadPercentage(
            parseInt(
              Math.round((progressEvent.loaded * 100) / progressEvent.total)
            )
          );
        }
      });
      setData(res.data);
      console.log(setData);
      setTableData(false);
      
      // Clear percentage
      setTimeout(() => setUploadPercentage(0), 10000);

      const { fileName, filePath } = res.data;

      setUploadedFile({ fileName, filePath });

      setMessage('Transcript Uploaded');
    } catch (err) {
      if (err.response.status === 500) {
        setMessage('There was a problem with the server');
      } else {
        setMessage(err.response.data.msg);
      }
      setUploadPercentage(0)
    }
  };

  /* 
    - Columns is a simple array right now, but it will contain some logic later on. It is recommended by react-table to memoize the columns data
    - Here in this example, we have grouped our columns into two headers. react-table is flexible enough to create grouped table headers
  */
    const columns = useMemo(
      () => [
        {
          // first group - Schedule
          Header: "Schedule",
          // First group columns
          columns: [
            {
              Header: "Course",
              accessor: "course"
            },
            {
              Header: "Coursename",
              accessor: "coursename"
            },
            {
              Header: "Attempted",
              accessor: "attempted"
            },
            {
              Header: "Received",
              accessor: "received"
            }
            ,
            {
              Header: "Grade",
              accessor: "grade"
            }

          ]
        }
      ],
      []
    );

  return (
    <Fragment>
      {message ? <Message msg={message} /> : null}
      <form onSubmit={onSubmit}>
        <div className='custom-file mb-4'>
          <input
            type='file'
            className='custom-file-input'
            id='customFile'
            onChange={onChange}
          />
          <label className='custom-file-label' htmlFor='customFile'>
            {fileName}
          </label>
        </div>

        <Progress percentage={uploadPercentage} />

        <input
          type='submit'
          value='Upload'
          className='btn btn-primary btn-block mt-4'
        />
      </form>
      {uploadedFile ? (
        <div className='row mt-5'>
          <div className='col-md-6 m-auto'>
            <h3 className='text-center'>{uploadedFile.fileName}</h3>
            <img style={{ width: '100%' }} src={uploadedFile.filePath} alt='' />
          </div>
        </div>
      ) : null}
      {tableData ? (
         <p/>
         ):
         ( 
         <Table columns={columns} data={data} />
         )
      }
      
    </Fragment>
  );
};
 
export default FileUpload

Nginx 配置

server {
server_name 52.200.122.98;

# react app & front-end files
location / {
root /home/ubuntu/KUAutoadvisor/client/build/;
try_files $uri /index.html;
}

# node api reverse proxy // the /api/ is assuming your api routes start with that i.e. www.your-site.com/api/endpoint
location /api/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_pass http://localhost:5000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_max_temp_file_size 4096m;
}
}

解决建议
  • 检查Multer存储目录权限
    AWS服务器上client/public/uploads目录可能不存在或权限不足,导致Express无法写入文件。执行以下命令创建目录并设置权限:

    mkdir -p /home/ubuntu/KUAutoadvisor/client/public/uploads
    chmod -R 755 /home/ubuntu/KUAutoadvisor/client/public/uploads
    chown -R ubuntu:ubuntu /home/ubuntu/KUAutoadvisor/client/public/uploads
    
  • 修正Multer路径问题
    相对路径在生产环境可能不可靠,改用绝对路径并确保目录存在:

    const uploadDir = path.resolve(__dirname, '../client/public/uploads');
    // 自动创建目录(如果不存在)
    if (!fs.existsSync(uploadDir)) {
      fs.mkdirSync(uploadDir, { recursive: true });
    }
    const storage = multer.diskStorage({
      destination: function (req, file, cb) {
        cb(null, uploadDir)
      },
      filename: function (req, file, cb) {
        cb(null, file.originalname)
      }
    })
    
  • 调整超时设置
    你的代码中有20秒延迟,加上文件上传和API请求时间,可能触发超时。在Express中显式设置更长超时:

    app.set('timeout', 300000); // 设置为5分钟
    

    Nginx的proxy_read_timeout 300s已经足够,但确保没有其他隐性超时限制。

  • 排查Express错误日志
    增强错误处理,输出详细错误信息,便于定位问题:

    catch (err) 
    {
      console.error('上传失败详情:', err);
      return res.status(500).json({ msg: 'An error occurred', error: err.message })
    }
    

    同时查看Express的控制台输出,确认是否有文件读取失败、docparser API调用错误等问题。

  • 直接测试Express接口
    跳过Nginx,在服务器上用curl直接测试上传接口,判断问题出在Express还是Nginx:

    curl -X POST -F "file=@/path/to/test/file" http://localhost:5000/api/upload
    

    如果请求正常,说明问题在Nginx配置;如果失败,说明Express端存在问题。

  • 检查Nginx请求转发
    在Nginx的/api/ location块中添加proxy_set_header X-Real-IP $remote_addr;,便于调试请求来源。同时确认Nginx有权限转发到localhost:5000,没有防火墙或安全组限制。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 04:01:24