事の起こり
管理画面を作っていた際、ファイルアップロードフォームで、
ファイルをD&Dで入力出来る領域を作って、postデータ内にJSONで埋め込んで送信しようとしていた所、firefoxでは送信出来るのにChromeでは送信出来なかった
現象
紆余曲折調べた所、同じファイルを送っているのにChromeだけ、ContentLengthが小さかった。
再現条件
- 動的にinput要素を作成(document.createElement("input"))している
- 要素のvalueが特定サイズ以上。
- http://www.w3schools.com/tags/att_input_maxlength.asp maxlengthの値と足りないときのContentLengthが同じなのでこの数値に起因しそう(同僚某氏thx
- 最初からHTMLに書いてあるInput要素のvalueにセットする場合には問題ない。
以上の条件で再現。
再現コード
postを受けるサーバが必要なのでPlackで実装。
9MB相当の文字列を作成して、HTML内に書き込んだ。
パス説明
OKパターンのポストをするページ、NGパターンのポストをするページ、ポストされてきた内容から必要な物を出力するページを作った。
キー名 | パス説明 |
---|---|
/ok | 9MB相当の文字列をHTML内に記述したinput 要素にJavaScriptで入力 |
/ng | 9MB相当の文字列をJavaScriptでcreateElement("input")で作成したinput 要素にJavaScriptで入力 |
/post | 上記二点のアップロード先。ページ内にContentLengthを表示する。 |
sample.psgi
use Plack::Request;
sub {
my $req = Plack::Request->new($_[0]);
my $s = "h" x 9_000_000;
my $ok_html = <<"EOS";
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
<form action="/post" method="post">
<input type="hidden" name="name" id="input" value="">
<button>push</button>
<script>
var input = document.getElementById("input")
input.value = "$s";
</script>
</form>
</body>
</html>
EOS
my $ng_html = <<"EOS";
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
<form id="form" action="/post" method="post">
<button>push</button>
</form>
<script>
var input = document.createElement("input")
input.name = "name"
input.value = "$s";
input.type = "hidden";
document.getElementById("form").appendChild(input)
</script>
</body>
</html>
EOS
if($req->path eq "/ok") {
$req->param('');
return [200, ["Content-Type" => "text/html"], [$ok_html]];
}
if($req->path eq "/ng") {
$req->param('');
return [200, ["Content-Type" => "text/html"], [$ng_html]];
}
if($req->path eq "/post") {
return [200, ["Content-Type" => "text/plain"], ['200 OK' , " " , $req->env->{CONTENT_LENGTH}]];
}
return [404 , ["Content-Type" => "text/plain"], ['OTHER NG']];
};
結果
ブラウザ | パス | 結果 | 可否 |
---|---|---|---|
firefox | /ok | 200 OK 9000005 | ○ |
firefox | /ng | 200 OK 9000005 | ○ |
firefox | /ok | 200 OK 9000005 | ○ |
firefox | /ng | 200 OK 524293 | × |
対応
とりあえずInput要素は作っておいてJavascriptではValueに入力するようにした。
maxlengthってフォームで入力出来る文字数であってバイト数ではなかったと記憶しているが、滅多に使わないのでそんな物だったのかなあと思いつつ…