php - 在 AWS EC2 上通过 HTML 表单中止上传大文件 20 秒 (ERR_CONNECTION_ABORTED)
问题描述
我正在尝试使用带有file
typeinput
元素的标准 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 上运行。
解决方案
我在使用 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;
推荐阅读
- html - 评论字段是必需的
- python-3.x - 如何避免使用 pd.to_excel() 方法更改日期?
- django - 表达式包含混合类型:DecimalField、IntegerField。您必须设置 output_field
- javascript - 如果可用,我可以从微距相机创建流吗?
- javascript - 如何使用反应查询在按钮单击时调用相关 API?
- java - Java EventEmitter客户端重新连接不起作用
- php - 在 Woocommerce 中显示和设置含税和不含税的价格
- node-red - 在不同的 JSON 对象中合并超过 3 个 csv 文件 node-red
- javascript - 如何使 JavaScript 中添加的元素像 HTML 中添加的元素一样响应 onclick?
- java - 返回 MaterialTimePicker