首页 > 解决方案 > 在 AWS EC2 上通过 HTML 表单中止上传大文件 20 秒 (ERR_CONNECTION_ABORTED)

问题描述

我正在尝试使用带有filetypeinput元素的标准 HTML 表单。此表单适用于小文件,但如果上传过程花费的时间超过 20 秒,则连接会断开并且上传会过早结束。

基础设施如下: 1 个 VPC,包含 1 个运行 Amazon Linux 的 EC2 t2.micro 实例,安装了 Apache 2.4/PHP 7.3/MySQL,连接到 1 个 EFS 挂载,可通过弹性 IP 访问。

我昨天花了一整天时间,今天花了更多时间试图弄清楚这一点。我认为 EC2 -> Limits 是它,因为它设置为 20,但这是指实例数,而不是连接时间限制。

php.ini相应地设置了指令:

max_execution_time = 900
post_max_size = 1048M
upload_max_filesize = 1024M
max_input_time = -1

是的,代码在非 AWS 服务器上运行良好。我正在使用您可以制作的最基本的 HTML 上传器进行测试:

<?php
if ($_FILES) {
 move_uploaded_file($_FILES['wtf']['tmp_name'],
 /localpath/'.basename($_FILES['wtf']['name']));
}
?>
<html>
<body>
<form method="post" enctype="multipart/form-data">
<input type="file" name="wtf">
<input type="submit">
</form>
</body>
</html>

这将返回ERR_CONNECTION_ABORTED消息。当然,这对于在 20 秒截止时间内上传的所有文件都非常有效。

我尝试安装交换内存(默认的 Amazon Linux AMI 没有),添加负载均衡器并将其编辑idle_timeout为 600 秒。我尝试过不同大小的文件,但它与大小无关,这与处理所需的时间有关。毫无疑问,它每次都会在大约 20 秒时中止。

过去我在使用 AWS 时遇到过类似的问题,当时,它归结为 ELB(负载均衡器)的“粘性”。我最初并没有在我测试代码的 EC2 实例上使用负载均衡器,所以当我启用一个时,该Edit Stickiness选项似乎只适用于经典负载均衡器。负载均衡器提供的当前明显可比较的设置是idle timeout(可从 EC2 -> 负载均衡器 -> [选择负载均衡器] -> 描述选项卡 -> 属性访问)。


12 天后的额外测试:

这仍然是一个问题。我已经尝试了很多东西,但没有任何东西可以使它起作用。

我尝试使用 curl 调用上面提供的基本上传器。我创建了一个空白文件并尝试将其提交到表单:

dd if=/dev/zero of=testfile.txt count=502400 bs=1024
curl -k -i -X POST -H "Content-Type: multipart/form-data" /
 -F "file_field=@/var/www/html/testfile.txt" / 
 https://domain.tld/test_upload.php

同样,它与文件的大小无关,它与时间有关。在慢速连接上,我无法上传 25 MB 的文件,直接在服务器上,我可以上传 400 MB 的文件,但它会在高于此的某个地方切断。同样,它总是在 15 到 20 秒后结束。

我也尝试通过 localhost 上传,也存在同样的问题,所以这似乎是服务器配置问题,除非有未知的 AWS 层在起作用。

curl -k -i -X POST -H "Content-Type: multipart/form-data" /
 -F "file_field=@/var/www/html/testfile.txt" /
 localhost/test_upload.php

上传时间超过 15/20 秒的 $_FILES 返回状态:

Array
(
    [name] => testfile.txt
    [type] =>
    [tmp_name] =>
    [error] => 3
    [size] => 0
)

错误代码是 ERR_CONNECTION_ABORTED,这导致我查找其他人在 EC2 上传时遇到的类似问题,但没有任何建议有帮助。

我在 php.ini 中尝试过的指令:

post_max_size = 1048M
upload_max_filesize = 1024M
memory_limit = 512M
max_input_time = 3600
max_execution_time 900
ignore_user_abort = On
upload_tmp_dir = /var/www/custom_tmp

有人建议默认 tmp 文件夹可能有导致问题的限制,但尝试将其更改为其他文件夹并不会影响它。

正如其他人所提到的,memory_limit 与任何这些都没有关系,但我还是尝试了它。

我在 httpd.conf 中尝试过的指令:

AllowOverRide = All
TimeOut 1200
KeepAlive On / Off
KeepAliveTimeout 1200

KeepAlive 用于通过同一个 TCP 连接发出多个请求,因此不完全相关,但无论哪种方式,打开或关闭,它都没有影响。

上传时,我观察了正在写入 tmp 文件夹中的文件。它会继续增长,直到遇到相同的超时,然后突然被删除,html 表单返回 ERR_CONNECTION_ABORTED 错误。

我尝试将 php.ini 的副本放在基本 HTML 文件夹中,但没有任何乐趣。我已经在 .htaccess 文件中设置了 Apache 指令,同样没有变化。我尝试使用 HTTP 连接而不是 HTTPS。没有什么帮助。

这是一个标准的 Amazon Linux AMI (HVM) t2.micro 实例。我不认为它是一个微型应该重要,我已经在 t1.micro 上设置了一个上传服务器,之前上传大文件没有任何问题,一些区别是它不在 VPC 中,它是在 AWS Classic 上运行。

标签: phpamazon-web-servicesamazon-ec2file-upload

解决方案


我在使用 EBS 多容器、NLB、.NET 堆栈时遇到了类似的问题。小文件 (<25M) 可以正常发布。大文件(最大 350MB)静默失败。完全没有响应,API端点没有被调用,Postman报socket挂断,CURL报错误52。

为了解决这个问题,我从网络负载均衡器 (NLB) 切换到应用程序负载均衡器 (ALB)。我还应用了这篇文章在 AWS Elastic Beanstalk 上的 Nginx conf 中增加 client_max_body_size 中 的设置,但将最大值设置为 350M。我还添加了设置以增加超时和缓冲区。被注入的 proxy.conf 的最终内容部分是:

    content: |
       client_max_body_size    350m;
       client_body_buffer_size 128k;
       proxy_connect_timeout   360;
       proxy_send_timeout      360;
       proxy_read_timeout      360;

推荐阅读