使用 PHP 手动解析原始多部分/表单数据数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5483851/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Manually parse raw multipart/form-data data with PHP
提问by Christof
I can't seem to find a real answer to this problem so here I go:
我似乎无法找到这个问题的真正答案,所以我开始了:
How do you parse raw HTTP request data in multipart/form-data
format in PHP? I know that raw POST is automatically parsed if formatted correctly, but the data I'm referring to is coming from a PUT request, which is not being parsed automatically by PHP. The data is multipart and looks something like:
你如何multipart/form-data
在 PHP 中以格式解析原始 HTTP 请求数据?我知道如果格式正确,原始 POST 会自动解析,但我所指的数据来自 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
I'm sending the data with libcurl like so (pseudo code):
我像这样用 libcurl 发送数据(伪代码):
curl_setopt_array(
CURLOPT_POSTFIELDS => array(
'user_id' => 3,
'post_id' => 5,
'image' => '@/tmp/current_file'),
CURLOPT_CUSTOMREQUEST => 'PUT'
);
If I drop the CURLOPT_CUSTOMREQUEST bit, the request is handled as a POST on the server and everything is parsed just fine.
如果我删除 CURLOPT_CUSTOMREQUEST 位,请求将作为服务器上的 POST 处理,并且一切都被解析得很好。
Is there a way to manually invoke PHPs HTTP data parser or some other nice way of doing this? And yes, I have to send the request as PUT :)
有没有办法手动调用 PHP 的 HTTP 数据解析器或其他一些不错的方法?是的,我必须将请求作为 PUT 发送:)
采纳答案by Christof
Edit - please read first:this answer is still getting regular hits 7 years later. I have never used this code since then and do not know if there is a better way to do it these days. Please view the comments below and know that there are many scenarios where this code will not work. Use at your own risk.
编辑 - 请先阅读:这个答案在 7 年后仍然经常点击。从那以后我再也没有使用过这段代码,也不知道这些天是否有更好的方法来做到这一点。请查看下面的评论并了解此代码在许多情况下不起作用。使用风险自负。
--
——
Ok, so with Dave and Everts suggestions I decided to parse the raw request data manually. I didn't find any other way to do this after searching around for about a day.
好的,根据 Dave 和 Everts 的建议,我决定手动解析原始请求数据。在搜索了大约一天后,我没有找到任何其他方法来做到这一点。
I got some help from this thread. I didn't have any luck tampering with the raw data like they do in the referenced thread, as that will break the files being uploaded. So it's all regex. This wasnt't tested very well, but seems to be working for my work case. Without further ado and in the hope that this may help someone else someday:
我从这个线程中得到了一些帮助。我没有像他们在引用的线程中那样篡改原始数据,因为这会破坏正在上传的文件。所以都是正则表达式。这没有经过很好的测试,但似乎适用于我的工作案例。事不宜迟,希望有一天这可以帮助其他人:
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];
}
}
Usage by reference (in order not to copy around the data too much):
引用使用(为了不复制太多数据):
$a_data = array();
parse_raw_http_request($a_data);
var_dump($a_data);
回答by Mahn
I'm surprised no one mentioned parse_str
or mb_parse_str
:
我很惊讶没有人提到parse_str
或mb_parse_str
:
$result = [];
$rawPost = file_get_contents('php://input');
mb_parse_str($rawPost, $result);
var_dump($result);
回答by jas-
I used Chris's example function and added some needed functionality, such as R Porter's need for array's of $_FILES. Hope it helps some people.
我使用了Chris的示例函数并添加了一些需要的功能,例如R Porter需要 $_FILES 的数组。希望它可以帮助一些人。
Here is the class& example usage
<?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']);
}
}
}
回答by Evert
I would suspect the best way to go about it is 'doing it yourself', although you might find inspiration in multipart email parsers that use a similar (if not the exact same) format.
我怀疑最好的方法是“自己做”,尽管您可能会在使用类似(如果不是完全相同)格式的多部分电子邮件解析器中找到灵感。
Grab the boundary from the Content-Type HTTP header, and use that to explode the various parts of the request. If the request is very large, keep in mind that you might store the entire request in memory, possibly even multiple times.
从 Content-Type HTTP 标头中获取边界,并使用它来分解请求的各个部分。如果请求非常大,请记住您可能会将整个请求存储在内存中,甚至可能多次。
The related RFC is RFC2388, which fortunately is pretty short.
相关的 RFC 是RFC2388,幸运的是它很短。
回答by Ben
I haven't dealt with http headers much, but found this bit of code that might help
我没有太多处理 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("##代码##")', 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;
}
From http://php.net/manual/en/function.http-parse-headers.php
回答by Dave Kok
Have you looked at fopen("php://input")
for parsing the content?
您是否查看fopen("php://input")
了解析内容?
Headers can also be found as $_SERVER['HTTP_*']
, names are always uppercased and dashes become underscores, eg $_SERVER['HTTP_ACCEPT_LANGUAGE']
.
标题也可以找到$_SERVER['HTTP_*']
,名称总是大写,破折号变成下划线,例如$_SERVER['HTTP_ACCEPT_LANGUAGE']
。