使用PHP手动解析原始多部分/表单数据
我似乎找不到这个问题的真正答案,因此我决定: 如何在PHP中解析使用PHP手动解析原始多部分/表单数据,php,http,parsing,curl,Php,Http,Parsing,Curl,我似乎找不到这个问题的真正答案,因此我决定: 如何在PHP中解析multipart/formdata格式的原始HTTP请求数据?我知道如果格式正确,原始帖子会自动解析,但我所指的数据来自PUT请求,PHP不会自动解析。数据是多部分的,看起来像: ------------------------------b2449e94a11c Content-Disposition: form-data; name="user_id" 3 ------------------------------b244
multipart/formdata
格式的原始HTTP请求数据?我知道如果格式正确,原始帖子会自动解析,但我所指的数据来自PUT请求,PHP不会自动解析。数据是多部分的,看起来像:
------------------------------b2449e94a11c
Content-Disposition: form-data; name="user_id"
3
------------------------------b2449e94a11c
Content-Disposition: form-data; name="post_id"
5
------------------------------b2449e94a11c
Content-Disposition: form-data; name="image"; filename="/tmp/current_file"
Content-Type: application/octet-stream
�����JFIF���������... a bunch of binary data
我使用libcurl发送数据,如下所示(伪代码):
如果我删除CURLOPT_CUSTOMREQUEST位,请求将作为POST在服务器上处理,所有内容都会被解析
有没有一种方法可以手动调用PHPs HTTP数据解析器,或者其他一些很好的方法?
是的,我必须以PUT的形式发送请求:)我没有太多地处理http头,但找到了一些可能有用的代码
function http_parse_headers( $header )
{
$retVal = array();
$fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
foreach( $fields as $field ) {
if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
$match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
if( isset($retVal[$match[1]]) ) {
$retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
} else {
$retVal[$match[1]] = trim($match[2]);
}
}
}
return $retVal;
}
函数http\u parse\u头($header)
{
$retVal=array();
$fields=explode(“\r\n”,preg_replace('/\x0D\x0A[\x09\x20]+/','','',$header));
foreach($fields作为$field){
if(preg_match('/([^::]+):(.+)/m',$field,$match)){
$match[1]=preg_replace('/(?你看过fopen('php://input“,“r”)
用于解析内容
标题也可以作为$\u SERVER['HTTP.*']
找到,名称总是大写,破折号变成下划线,例如$\u SERVER['HTTP.'u ACCEPT.'u LANGUAGE']
我认为最好的方法是“自己动手”,尽管你可能会从使用类似方法的多部分电子邮件解析器中找到灵感(如果不完全相同)格式
从内容类型HTTP头中获取边界,并使用该边界分解请求的各个部分。如果请求非常大,请记住,您可能会将整个请求存储在内存中,甚至多次
相关的RFC是,幸运的是,它很短。编辑-请先阅读:这个答案在7年后仍然经常被点击。从那时起,我从未使用过这个代码,也不知道这些天是否有更好的方法。请查看下面的评论,并知道有很多情况下这个code不起作用。使用风险自负
--
好的,根据Dave和Everts的建议,我决定手动解析原始请求数据。在搜索了大约一天之后,我没有找到任何其他方法
我从中得到了一些帮助。我没有像在引用线程中那样篡改原始数据,因为这会破坏上传的文件。所以都是正则表达式。这没有经过很好的测试,但似乎适用于我的工作案例。没有进一步的麻烦,希望有一天这可以帮助其他人:
function parse_raw_http_request(array &$a_data)
{
// read incoming data
$input = file_get_contents('php://input');
// grab multipart boundary from content type header
preg_match('/boundary=(.*)$/', $_SERVER['CONTENT_TYPE'], $matches);
$boundary = $matches[1];
// split content by boundary and get rid of last -- element
$a_blocks = preg_split("/-+$boundary/", $input);
array_pop($a_blocks);
// loop data blocks
foreach ($a_blocks as $id => $block)
{
if (empty($block))
continue;
// you'll have to var_dump $block to understand this and maybe replace \n or \r with a visibile char
// parse uploaded files
if (strpos($block, 'application/octet-stream') !== FALSE)
{
// match "name", then everything after "stream" (optional) except for prepending newlines
preg_match("/name=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/s", $block, $matches);
}
// parse all other fields
else
{
// match "name" and optional value in between newline sequences
preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $block, $matches);
}
$a_data[$matches[1]] = $matches[2];
}
}
按引用使用(为了不在数据周围复制太多):
我使用了的示例函数并添加了一些必要的功能,例如需要数组的$u文件。希望它能帮助一些人
下面是一个示例&示例
我很惊讶没有人提到parse_str
或mb_parse_str
:
$result = [];
$rawPost = file_get_contents('php://input');
mb_parse_str($rawPost, $result);
var_dump($result);
看看这个问题的python版本,了解一些想法:。基本上,您只需要拆分二进制数据,重新组合它们并重建原始文件。要解析简单的PDF表单,请尝试。我今天早些时候看到了该函数,但结果没有多大用处。您成功地使用了该函数吗?fopen()php://input')将只读取内容,而不解析它?我希望解析的值不在$u服务器变量中。使用mod_rewrite将其重定向为POSTnevermind如何,与只执行代码的R标志混淆了。但是您可以通过重建HTTP请求用PHP重定向它,但将其修改为POST请求并调用另一个script解析请求。如何将请求重写为POST?这必须在服务器上进行。您可以在端口80上打开服务器的套接字并向其提供请求。响应可以通过readfile发送回客户端。添加连接:close header以在处理完请求后关闭连接。嗯,这也是Dave Kok写的。我想我必须检查一下。问题是,我的请求内容与我预期的内容类型边界不太一样。我在最初的问题中粘贴了一点。你知道为什么它看起来是这样吗?实际的边界不是列在每个部分的标题中,而是列在顶部标题中。因此无法通过访问php://input,但正如dave提到的,它应该位于$\u服务器['HTTP\u CONTENT\u TYPE']或$\u服务器['CONTENT\u TYPE']属性中。如果post变量包含数组,此函数将不起作用。例如,“value[id]”的名称将无法正确解析。内容处置:表单数据;name=“elements[\u itemname][value]”内容配置:表单数据;name=“数组[值]”--这两种方法都不适用。没错。我不需要嵌套数组。谢谢。这对我帮助很大。只是修改为通过这些部分之间的两个换行来分隔标题/内容,而不是内容类型。我认为这涵盖了标准better@Chris我做了一个修改版本来覆盖嵌套数组,下面是代码解析遗憾的是,HTTP数据比此代码复杂得多。它可能在某些情况下有效,但在许多其他情况下无效。例如,在实际内容之前可能有多行(例如“content Length:XXX”,此代码不处理。边界的破折号数量可能因内容类型和输入中的内容而异。此外,代码也不处理存在但没有值的键。我想这对我不起作用,因为我使用的是带有multipart/form data
CONTENT TYPE的表单中的二进制文件。FWMC问题是特别是关于MIME类型为multipart/form data
,而不是application/x-www-form-urlencoded
,这就是parse_str()
$a_data = array();
parse_raw_http_request($a_data);
var_dump($a_data);
<?php
include_once('class.stream.php');
$data = array();
new stream($data);
$_PUT = $data['post'];
$_FILES = $data['file'];
/* Handle moving the file(s) */
if (count($_FILES) > 0) {
foreach($_FILES as $key => $value) {
if (!is_uploaded_file($value['tmp_name'])) {
/* Use getimagesize() or fileinfo() to validate file prior to moving here */
rename($value['tmp_name'], '/path/to/uploads/'.$value['name']);
} else {
move_uploaded_file($value['tmp_name'], '/path/to/uploads/'.$value['name']);
}
}
}
$result = [];
$rawPost = file_get_contents('php://input');
mb_parse_str($rawPost, $result);
var_dump($result);