阿里云函数计算如何获取$_FILE

入口函数,无法直接使用$_POST $_GET或者$_FILE获取数据,
$request->getBody()->getContents();
获取到的POST内容直接是 : aa=11&bb=11&cc=11
现在使用:
<form action="http://xxxxxx" method="post" enctype="multipart/form-data" >
	<input type="file" name="png">
	<button type="submit">上传</button>
</form>

打印出来的 接收的body内容为,怎么给他转换成$_FILE
053b9e62f1a877c7b921ae791c1e5515167457.png


<?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|隐藏机器人聊天)
  • 000
    @Ta / 2019-07-08 / /
    没用过 或者换个思路 把文件内容通过js转base64提交,至于post参数的字符串 用parse_str()转换为数组试试,不过鉴于云函数没有自动序列url参数  干脆直接改json协议提交json字符串
  • 000
    @Ta / 2019-07-08 / /

    云函数的公网流量是不是只能按0.8元/GB收费,能开带宽或购置流量包?@胡椒舰长
    斗图专属版

  • @Ta / 2019-07-08 / /
    @000,函数计算如果是http触发器,只能是按照流量收费,或者你可以绑定弹性网卡。
  • 000
    @Ta / 2019-07-08 / /

    哦~ 不过想来带宽本来也贵,轻度使用还是按流量划算点
    斗图专属版

  • @Ta / 2019-07-08 / /

    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)]);
    }
    
  • @Ta / 2019-07-08 / /

    至于POST和GET数据,自己按&和=分割一下,然后urldecode就可以得到结果了。
    更新1:其实只要使用一个parse_str函数就可以了。

  • @Ta / 2019-07-08 / /

    找到一个更通用的代码,传入$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
    @Ta / 2019-07-09 / /

    @老虎会游泳,parse_str 我有说了呢 而且说真的 这种看着就不稳的解析文件方式真心不如将二进制文件转base64 后端再还原 来的省心省力,这阿里的函数计算看着疑似cli模式运行的php...
    斗图专属版

  • @Ta / 2019-07-09 / /

    @000multipart/form-data可能是在浏览器中上传文件时唯一好用的方式。其他方式(无论用什么编码)都会大大增加所需发送的数据量,并且需要编写前端程序(不能使用form表单。或者你也可以选择HTTP PUT发送整个文件内容,但是这样就不能附带任何其他数据了(其他数据只能通过查询字符串提交,倒是也可以,但是,依然需要前端程序),并且也不能同时上传多个文件。目前唯一可以上传多个文件的form表单提交数据类型是multipart/form-data)。

    而且,multipart/form-data实在是一个简单到不能再简单的封装,以任意"--分隔符"分隔多个部分(可能是文件或者POST数据),以"\r\n"换行,以"--分隔符--"结束,然后每个部分内嵌HTTP风格的Header和Body。它的封装和解封装代码都可以实现的万分稳定,并且很显然浏览器和Web服务器早就稳定的实现他们很多年了。我只是懒得写一个实现相同功能的稳定代码,所以网上找了两个。那个完全依靠正则表达式的实现可能确实不怎么稳定(只能支持上传特定MIME类型的文件),但是后面那个实现一看就相当好,因为它完全是字符串分割操作(当然也稍微有一点模式匹配。但是HTTP协议的解析就是这样,在按"\r\n"分割行之后,你要么按:分割切分头域及其内容,要么就直接匹配自己关心的头域就可以了。在一行内的匹配没有什么稳定性问题),而这也是Web服务器在实现相同内容解析的时候会做的事情。

  • @Ta / 2019-07-09 / /

    @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只增加固定长度的分隔符和头信息,解复用只需要字符串分割,而且分割算法可以实现的很简单)。

  • 000
    @Ta / 2019-07-09 / /

    @老虎会游泳,你说的我当然都懂,我之所以这样说也正是因为环境没有相应支持的情况下自己模拟服务器处理raw数据比较麻烦了,base64编码后体积大1/3也是一直了解的,但使用足够方便(别的app接口我就看过不少是这样处理图片上传的 大概单纯是为了方便了),百度一个base64 JS函数也更容易一点~ 我加的好些前端群也有不少人(非通常场景下)提过这样处理上传,我只是现学现卖了
    斗图专属版

  • 000
    @Ta / 2019-07-09 / /

    至于性能与人性化使用哪个优先级更高的老生长谈问题早有定论,无非是看使用场景,否则高级语言都没有存在的必要了。另外现在前端最流行的ajax提交处理都是用的axios库,提交的raw就是json数据,PHP无法通过post,get全局变量获取,而是直接json decode post过来的raw数据
    斗图专属版

  • @Ta / 2019-07-09 / /
    你们在讨论什么。完全看不懂
    9e055b12a27f56ffdad1345439199cb0
    来自 Redmi Note5
  • @Ta / 2019-07-09 / /

    @方妹,讨论如何编写服务器代码来接收从网页表单上传的文件。

  • @Ta / 2019-07-15 / /
    参考 PSR-7

    [psr.phphub.org/「PSR 规范」PSR-7 HTTP 消息接口规范.md at master · summerblue/psr.phphub.org](https://github.com/summerblue/psr.phphub.org/blob/master/psrs/%E3%80%8CPSR%20%E8%A7%84%E8%8C%83%E3%80%8DPSR-7%20HTTP%20%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3%E8%A7%84%E8%8C%83.md#16-uploaded-files)


    以及官方文档

    [PHP 函数入口_PHP_编程语言_构建函数_函数计算-阿里云](https://help.aliyun.com/document_detail/89029.html?spm=a2c4g.11174283.6.570.20685212IgcVRq#h2-http-5)
添加新回复
回复需要登录