标题: 阿里云函数计算如何获取$_FILE
时间: 2019-07-08
<form action="http://xxxxxx" method="post" enctype="multipart/form-data" >
<input type="file" name="png">
<button type="submit">上传</button>
</form>
<?php
use RingCentral\Psr7\Response;
/*
if you open the initializer feature, please implement the initializer function, as below:
function initializer($context) {
echo 'initializing' . PHP_EOL;
}
*/
function handler($request, $context): Response{
$body = $request->getBody()->getContents();
$queries = $request->getQueryParams();
$method = $request->getMethod();
$headers = $request->getHeaders();
$path = $request->getAttribute('path');
$requestURI = $request->getAttribute('requestURI');
$clientIP = $request->getAttribute('clientIP');
return ret($body);
return ret(['$clientIP'=>$clientIP,'$body'=>$body,'post'=>json_encode($_POST)]);
}
function ret($response)
{
$data= is_array($response)?json_encode($response,JSON_UNESCAPED_UNICODE):$response;
return new Response(
200,
array(
'Content-Type' => 'text/html; charset=utf-8',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
),
$data
);
}
『回复列表(15|隐藏机器人聊天)』
http://www.it1352.com/885600.html
使用PHP手动解析原始multipart/form-data数据
function parse_raw_http_request($content_type, $body)
{
$a_data = [];
// grab multipart boundary from content type header
preg_match('/boundary=(.*)$/', $content_type, $matches);
$boundary = $matches[1];
// split content by boundary and get rid of last -- element
$a_blocks = preg_split("/-+$boundary/", $body);
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];
}
return $a_data;
}
function handler($request, $context): Response{
$body = $request->getBody()->getContents();
$queries = $request->getQueryParams();
$method = $request->getMethod();
$headers = $request->getHeaders();
$path = $request->getAttribute('path');
$requestURI = $request->getAttribute('requestURI');
$clientIP = $request->getAttribute('clientIP');
return ret($body);
return ret(['$clientIP'=>$clientIP,'file'=>parse_raw_http_request($headers['content-type']/*这里自己改,看你怎么找到Content-Type*/, $body)]);
}
找到一个更通用的代码,传入$content_type
和$body
,它会为你自动设置$_POST和$_FILES。
你得自己从$headers
里面找到content_type
,并且自己确认可以从parse_input
函数外部获得它设置的$_POST和$_FILES值,如果得不到,需要用return返回值。
至于$_GET,用parse_str(parse_url($requestURI, PHP_URL_QUERY), $_GET);
就可以
<?php
function parse_input($content_type, $body){
$content_type = !empty($content_type) ? $content_type : 'application/x-www-form-urlencoded';
$tmp = explode( ';', $content_type );
$boundary = '';
$encoding = '';
$content_type = array_shift( $tmp );
foreach( $tmp as $t ){
if( strpos( $t, 'boundary' ) !== false ){
$t = explode( '=', $t, 2 );
if( isset( $t[ 1 ] ) )
$boundary = '--' . $t[1];
}
else if( strpos( $t, 'charset' ) !== false ){
$t = explode( '=', $t, 2 );
if( isset( $t[ 1 ] ) )
$encoding = $t[1];
}
if( $boundary !== '' && $encoding !== '' )
break;
}
switch( $content_type ){
case 'multipart/form-data':
#grab multipart boundary from content type header
if( !empty( $boundary ) )
break;
$this->content_type = 'application/x-www-form-urlencoded';
case 'application/x-www-form-urlencoded':
parse_str( $body, $_POST );
return;
default:
return;
}
$_FILES = array();
$_POST = array();
$chunkLength = 8096;
$raw_headers = '';
$stream = $body;
$sanity = fgets( $stream, strlen( $boundary ) + 5 );
if( rtrim( $sanity ) !== $boundary ) #malformed file, boundary should be first item
return;
while( ( $chunk = fgets( $stream ) ) !== false ){
if( $chunk === $boundary )
continue;
if( rtrim( $chunk ) == '' ){ #blank line means we have all the headers and are going to read content
$raw_headers = explode( "\r\n", $raw_headers );
$headers = array();
$matches = array();
foreach( $raw_headers as $header ){
if( strpos( $header, ':' ) === false )
continue;
list( $name, $value ) = explode( ':', $header, 2 );
$headers[ strtolower( $name ) ] = ltrim( $value, ' ' );
}
$raw_headers = '';
if( !isset( $headers[ 'content-disposition' ] ) )
continue;
$filename = NULL;
preg_match(
'/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/',
$headers['content-disposition'],
$matches
);
list( , $type, $name ) = $matches;
#process data
if( isset( $matches[ 4 ] ) ){ #pull in file
$error = UPLOAD_ERR_OK;
$filename = $matches[ 4 ];
$filename_parts = pathinfo( $filename );
$contentType = 'unknown';
if( isset( $headers[ 'content-type' ] ) ){
$tmp = explode( ';', $headers[ 'content-type' ] );
$contentType = $tmp[0];
}
$tmpnam = tempnam( ini_get( 'upload_tmp_dir' ), 'php' );
$fileHandle = fopen( $tmpnam, 'wb' );
if( $fileHandle === false )
$error = UPLOAD_ERR_CANT_WRITE;
else{
$lastLine = NULL;
while( ( $chunk = fgets( $stream, $chunkLength ) ) !== false && strpos( $chunk, $boundary ) !== 0 ) {
if( $lastLine !== NULL ){
if( fwrite( $fileHandle, $lastLine ) === false ){
$error = UPLOAD_ERR_CANT_WRITE;
break;
}
}
$lastLine = $chunk;
}
if( $lastLine !== NULL && $error !== UPLOAD_ERR_CANT_WRITE ){
if( fwrite( $fileHandle, rtrim( $lastLine, "\r\n" ) ) === false )
$error = UPLOAD_ERR_CANT_WRITE;
}
}
$items = array(
'name' => $filename,
'type' => $contentType,
'tmp_name' => $tmpnam,
'error' => $error,
'size' => filesize( $tmpnam )
);
$tmp = explode( '[', $name, 2 );
foreach( $items as $index => $item ){
$spec = $index;
if( count( $tmp ) > 1 )
$spec .= '[' . $tmp[1];
$t = $spec . '=' . $item;
parse_str( $t, $array2 );
$_FILES = recursive_setter( $spec, $_FILES, $array2 );
}
continue;
}
else{ #pull in variable
$fullValue = '';
$lastLine = NULL;
while( ( $chunk = fgets( $stream ) ) !== false && strpos( $chunk, $boundary ) !== 0 ){
if( $lastLine !== NULL )
$fullValue .= $lastLine;
$lastLine = $chunk;
}
if( $lastLine !== NULL )
$fullValue .= rtrim( $lastLine, "\r\n" );
if( isset( $headers[ 'content-type' ] ) ){
$tmp = explode( ';', $headers[ 'content-type' ] );
$encoding = '';
foreach( $tmp as $t ){
if( strpos( $t, 'charset' ) !== false ){
$t = explode( $t, '=', 2 );
if( isset( $t[ 1 ] ) )
$encoding = $t[1];
break;
}
}
if( $encoding !== '' && strtoupper( $encoding ) !== 'UTF-8' && strtoupper( $encoding ) !== 'UTF8' ){
$tmp = mb_convert_encoding( $fullValue, 'UTF-8', $encoding );
if( $tmp !== false )
$fullValue = $tmp;
}
}
$fullValue = $name . '=' . $fullValue;
$origName = $name;
$tmp = array();
parse_str( $fullValue, $tmp );
$_POST = recursive_setter( $origName, $_POST, $tmp );
}
continue;
}
$raw_headers .= $chunk;
}
fclose( $stream );
}
function recursive_setter( $spec, $array, $array2 ){
if( !is_array( $spec ) )
$spec = explode( '[', (string)$spec );
$currLev = array_shift( $spec );
$currLev = rtrim( $currLev, ']' );
if( $currLev !== '' ){
$currLev = $currLev . '=p';
$tmp = array();
parse_str( $currLev, $tmp );
$tmp = array_keys( $tmp );
$currLev = reset( $tmp );
}
if( !is_array( $array ) ){
$array = $array2;
}else if( $currLev === '' ){
$array[] = reset( $array2 );
}else if( isset( $array[ $currLev ] ) && isset( $array2[ $currLev ] ) ){
$array[ $currLev ] = recursive_setter( $spec, $array[ $currLev ], $array2[ $currLev ] );
}else if( isset( $array2[ $currLev ] ) ){
$array[ $currLev ] = $array2[ $currLev ];
}
return $array;
}
if( !is_array( $spec ) )
$spec = explode( '[', (string)$spec );
$currLev = array_shift( $spec );
$currLev = rtrim( $currLev, ']' );
if( $currLev !== '' ){
$currLev = $currLev . '=p';
$tmp = array();
parse_str( $currLev, $tmp );
$tmp = array_keys( $tmp );
$currLev = reset( $tmp );
}
if( !is_array( $array ) ){
$array = $array2;
}else if( $currLev === '' ){
$array[] = reset( $array2 );
}else if( isset( $array[ $currLev ] ) && isset( $array2[ $currLev ] ) ){
$array[ $currLev ] = self::recursive_setter( $spec, $array[ $currLev ], $array2[ $currLev ] );
}else if( isset( $array2[ $currLev ] ) ){
$array[ $currLev ] = $array2[ $currLev ];
}
return $array;
}
@000,multipart/form-data
可能是在浏览器中上传文件时唯一好用的方式。其他方式(无论用什么编码)都会大大增加所需发送的数据量,并且需要编写前端程序(不能使用form表单。或者你也可以选择HTTP PUT发送整个文件内容,但是这样就不能附带任何其他数据了(其他数据只能通过查询字符串提交,倒是也可以,但是,依然需要前端程序),并且也不能同时上传多个文件。目前唯一可以上传多个文件的form表单提交数据类型是multipart/form-data
)。
而且,multipart/form-data
实在是一个简单到不能再简单的封装,以任意"--分隔符"分隔多个部分(可能是文件或者POST数据),以"\r\n"换行,以"--分隔符--"结束,然后每个部分内嵌HTTP风格的Header和Body。它的封装和解封装代码都可以实现的万分稳定,并且很显然浏览器和Web服务器早就稳定的实现他们很多年了。我只是懒得写一个实现相同功能的稳定代码,所以网上找了两个。那个完全依靠正则表达式的实现可能确实不怎么稳定(只能支持上传特定MIME类型的文件),但是后面那个实现一看就相当好,因为它完全是字符串分割操作(当然也稍微有一点模式匹配。但是HTTP协议的解析就是这样,在按"\r\n"分割行之后,你要么按:
分割切分头域及其内容,要么就直接匹配自己关心的头域就可以了。在一行内的匹配没有什么稳定性问题),而这也是Web服务器在实现相同内容解析的时候会做的事情。
@000,建议你直接阅读RFC1867中文版《HTML中基于表单的文件上传》的“6.例子”部分来了解这个打包方式是多么简单。
https://blog.csdn.net/a972669015/article/details/86623831
我可以说,JSON的解码复杂度是这个方案的4倍(有复杂的嵌套结构、引号转义,并且还需要处理可能存在的unicode转义序列)。Base64的解码复杂度是这个方案的3倍(涉及非8bit数学运算),甚至HTTP查询字符串的解码复杂度都是它的1.5倍(就是parse_str
会做的事情,需要按&
和=
分割,处理名称中的数组xx[]
,反序列化%xx
串,并且还要处理特殊的+
字符)。你之所以用起来方便,只是因为你有现成的编码解码函数罢了。
准确的来说,multipart/form-data
只是一个封装,因为它并没有以任何形式改变你要传输的内容,它原样包含你要传输的内容,只是加上了头信息和分隔符。所以,从multipart/form-data
提取信息的操作叫做“解封装”,或者“解复用”,它并不是一种编码。所以multipart/form-data
的效率显然要比Base64编码好的多。当然,在文件比较小的时候编解码时间可能看不出来,流量多花一点可能也无所谓就是了(Base64编码后大小增加33%,并且解码时需要花很多CPU时间去做位运算,而multipart/form-data
只增加固定长度的分隔符和头信息,解复用只需要字符串分割,而且分割算法可以实现的很简单)。
至于性能与人性化使用哪个优先级更高的老生长谈问题早有定论,无非是看使用场景,否则高级语言都没有存在的必要了。另外现在前端最流行的ajax提交处理都是用的axios库,提交的raw就是json数据,PHP无法通过post,get全局变量获取,而是直接json decode post过来的raw数据
斗图专属版