userland multipart / form-data处理程序

时间:2011-04-06 03:19:32

标签: php multipartform-data

我正在寻找一个包含脚本/类的插件,用于剖析multipart/form-data并从中填充$_POST(+ raw)和$_FILES。通常PHP会自己做。但是因为自动处理对我来说不够,并使php://input无法使用 [1] 我可能会使用这样的东西来阻止它:

  

RewriteRule .* - [E=CONTENT_TYPE:noparsing/for-you-php]
   Does not work. Actual solution requires mod_headers and RequestHeader set...

提取程序可能不那么复杂。但我宁愿使用经过充分测试的解决方案。最重要的是,我更倾向于使用fgets进行拆分的实现,并且密切有效地模仿$_FILES处理。找到二进制有效负载的结束对我来说似乎相当棘手,特别是当您必须剥离\r\n但可能遇到仅发送\n的客户端时(不允许,但可能)。

我确定存在这样的事情。但是我很难用谷歌搜索它。有谁知道实施? (PEAR :: mimeDecode可以被黑客攻击以获得对表单数据的一种工作,但这是一种内存耗费。)

简单的用例:需要保留原始字段名称(包括空格和特殊字符),以便记录,但不能总是避免文件上传。


出于装饰目的,这就是POST请求的外观:

POST / HTTP/1.1
Host: localhost:8000
Content-Length: 17717
Content-Type: multipart/form-data; boundary=----------3wCuBwquE9P7A4OEylndVx

\r\n\r\n序列之后,multipart / payload遵循如下:

------------3wCuBwquE9P7A4OEylndVx
Content-Disposition: form-data; name="_charset_"

windows-1252
------------3wCuBwquE9P7A4OEylndVx
Content-Disposition: form-data; name=" text field \\ 1 \";inject=1"

text1 te twj sakfkl
------------3wCuBwquE9P7A4OEylndVx
Content-Disposition: form-data; name="file"; filename="dial.png"
Content-Type: image/png

IPNG Z @@@MIHDR@@B`@@B;HF@@@-'.e@@@AsRGB@.N\i@@@FbKGD@?@?@? ='S@@@     
@@@GtIMEGYAAU,#}BRU@@@YtEXtComment@Created with GIMPWANW@@ @IDATxZl]w|

3 个答案:

答案 0 :(得分:4)

现在已经很晚了,我现在无法对此进行测试,但以下内容应该可以满足您的需求:

//$boundary = null;

if (is_resource($input = fopen('php://input', 'rb')) === true)
{

    while ((feof($input) !== true) && (($line = fgets($input)) !== false))
    {
        if (isset($boundary) === true)
        {
            $content = null;

            while ((feof($input) !== true) && (($line = fgets($input)) !== false))
            {
                $line = trim($line);

                if (strlen($line) > 0)
                {
                    $content .= $line . ' ';
                }

                else if (empty($line) === true)
                {
                    if (stripos($content, 'name=') !== false)
                    {
                        $name = trim(stripcslashes(preg_replace('~.*name="?(.+)"?.*~i', '$1', $content)));

                        if (stripos($content, 'Content-Type:') !== false)
                        {
                            $tmpname = tempnam(sys_get_temp_dir(), '');

                            if (is_resource($temp = fopen($tmpname, 'wb')) === true)
                            {
                                while ((feof($input) !== true) && (($line = fgets($input)) !== false) && (strpos($line, $boundary) !== 0))
                                {
                                    fwrite($temp, preg_replace('~(?:\r\n|\n)$~', '', $line));
                                }

                                fclose($temp);
                            }

                            $FILES[$name] = array
                            (
                                'name' => trim(stripcslashes(preg_replace('~.*filename="?(.+)"?.*~i', '$1', $content))),
                                'type' => trim(preg_replace('~.*Content-Type: ([^\s]*).*~i', '$1', $content)),
                                'size' => sprintf('%u', filesize($tmpname)),
                                'tmp_name' => $tmpname,
                                'error' => UPLOAD_ERR_OK,
                            );
                        }

                        else
                        {
                            $result = null;

                            while ((feof($input) !== true) && (($line = fgets($input)) !== false) && (strpos($line, $boundary) !== 0))
                            {
                                $result .= preg_replace('~(?:\r\n|\n)$~', '', $line);
                            }

                            if (array_key_exists($name, $POST) === true)
                            {
                                if (is_array($POST[$name]) === true)
                                {
                                    $POST[$name][] = $result;
                                }

                                else
                                {
                                    $POST[$name] = array($POST[$name], $result);
                                }
                            }

                            else
                            {
                                $POST[$name] = $result;
                            }
                        }
                    }

                    if (strpos($line, $boundary) === 0)
                    {
                        //break;
                    }
                }
            }
        }

        else if ((is_null($boundary) === true) && (strpos($line, 'boundary=') !== false))
        {
            $boundary = "--" . trim(preg_replace('~.*boundary="?(.+)"?.*~i', '$1', $line));
        }
    }

    fclose($input);
}

echo '<pre>';
print_r($POST);
echo '</pre>';

echo '<hr />';

echo '<pre>';
print_r($FILES);
echo '</pre>';

答案 1 :(得分:3)

也许一个新的php.ini指令 enable_post_data_reading 可以提供帮助,但似乎它是在PHP 5.4中添加的,我仍然有早期版本所以无法测试它:(

来自PHP Manual

  

enable_post_data_reading 布尔值

     

禁用此选项会导致$ _POST   和$ _FILES不被填充。阅读postdata的唯一方法是   然后通过php://输入流包装器。这可能很有用   代理请求或在内存中高效处理POST数据   方式。

答案 2 :(得分:0)

阅读评论,如何在POST之前对数据进行编码?让客户端以UTF8或甚至URL编码发送POST数据,然后传输丢失的ASCII字符,而无需编写自己的POST处理程序,这很可能会引入自己的错误......