超1MB POST请求浏览器提交被截断,Postman提交正常
问题概述
用JavaScript的fetch发送POST请求提交超过1MB的表单数据,后端通过Perl/CGI脚本将数据写入文件时出现以下问题:
- Chrome/IE提交时,数据会在1MB处被截断,无法完整保存;
- 用Postman提交相同的原始数据则可成功保存;
- Postman和浏览器请求的
Content-Length均显示正确长度; - 返回响应对比:Postman能返回完整的6MB数据+分隔符,浏览器仅返回1MB数据;
- 提交略小于1MB的数据时,浏览器可正常上传保存;
- 浏览器网络面板显示完整的6MB请求,但无法确认数据是否完整到达后端,未找到明确的限制配置。
现有代码
JavaScript 上传函数
async function serverSave(data, access) { // Data to send in the POST request const formData = new FormData(); formData.append('access', access); formData.append('data', data); // URL endpoint to send the POST request to const url = 'http://www.mywebsite.com/cgi-bin/upload.pl'; // Options for the fetch request const options = { method: 'POST', body: formData }; // Send the POST request using fetch try{ response = fetch(url, options); const result = await response.text(); console.log('Response:', result); } catch { // Handle errors console.error('There was a problem saving to the server', error); }; }
Perl CGI 处理脚本
#!/usr/bin/perl use strict; use warnings; my $buffer = ''; if($ENV{CONTENT_LENGTH}) { read(STDIN,$buffer,$ENV{CONTENT_LENGTH}); } my ($data_value) = $buffer =~ /name="data"\s*?\n\s*?\n(.+?)\n-{5}/s; my ($access_value) = $buffer =~ /name="access"\s*?\n\s*?\n(.+?)\n-{5}/s; $access_value =~ s/[\r\n]+$//; $data_value =~ s/[\r\n]+$//; my $fn = "../zfiles/".$access_value.".json"; open(FH, '>', $fn) or die $!; print FH $data_value; close(FH); print "Content-type: text/plain\n\n"; print $buffer; print "OK"; print $ENV{CONTENT_LENGTH};
问题根源
- Perl脚本的STDIN读取逻辑缺陷:
直接使用read(STDIN, $buffer, $ENV{CONTENT_LENGTH})单次读取数据,在CGI环境下,当数据量较大时,系统会限制单次读取的缓冲区大小(通常为1MB),导致无法一次性读取完整的CONTENT_LENGTH字节数,最终$buffer只包含部分数据。 - 手动正则解析表单数据的风险:
用(.+?)\n-{5}这种非贪婪匹配解析multipart/form-data,当$buffer被截断后,无法找到完整的分隔符-----,导致匹配到不完整的data_value。 - JavaScript代码的小疏漏:
原代码中response = fetch(url, options)缺少await,会导致后续await response.text()处理的是未完成的Promise,虽然这不是本次数据截断的直接原因,但可能引发其他异步处理问题。
修复方案
1. 修复Perl的STDIN循环读取逻辑
替换单次read为循环读取,确保获取完整的请求数据:
my $buffer = ''; if ($ENV{CONTENT_LENGTH}) { my $remaining = $ENV{CONTENT_LENGTH}; while ($remaining > 0) { my $bytes_read = read(STDIN, $buffer, $remaining, length($buffer)); last unless defined $bytes_read; # 处理读取错误 $remaining -= $bytes_read; } }
2. 使用CGI.pm模块解析表单数据(推荐)
手动解析multipart/form-data极易出错,Perl自带的CGI.pm能正确处理表单数据,避免正则匹配的缺陷:
#!/usr/bin/perl use strict; use warnings; use CGI; my $cgi = CGI->new; my $access_value = $cgi->param('access'); my $data_value = $cgi->param('data'); # 清理末尾的换行符 $access_value =~ s/[\r\n]+$// if defined $access_value; $data_value =~ s/[\r\n]+$// if defined $data_value; my $fn = "../zfiles/" . $access_value . ".json"; open(FH, '>', $fn) or die $!; print FH $data_value; close(FH); print "Content-type: text/plain\n\n"; print $data_value; # 返回完整数据用于验证 print "OK"; print $ENV{CONTENT_LENGTH};
3. 修正JavaScript的异步处理
补全await并正确捕获错误参数:
async function serverSave(data, access) { const formData = new FormData(); formData.append('access', access); formData.append('data', data); const url = 'http://www.mywebsite.com/cgi-bin/upload.pl'; const options = { method: 'POST', body: formData }; try { const response = await fetch(url, options); const result = await response.text(); console.log('Response:', result); } catch (error) { console.error('There was a problem saving to the server', error); } }
验证步骤
- 上传超过1MB的数据,检查后端生成的json文件是否完整;
- 查看响应返回的内容是否与提交的数据一致;
- 确认
Content-Length数值与实际写入文件的大小匹配。
内容的提问来源于stack exchange,提问作者Chris ten Den
相关产品推荐
相关产品推荐

