请问workerman HTTP上传是怎么做的,能否给小白指点一下

helloio

问题描述

本人最近对workerMan很感兴趣,打算自己写一个和webman差不多的框架
(当然不是造轮子,只支持HTTP、HTTPS协议)
已经完成了90%,但是当我测试上传时一直行不通,总是进度在90%停住了,差200B但是查看保存的文件都正常
翻了翻WorkerMan源码,说实在的API太多本人没看明白,所以只能拜托作者了
恳请作者帮小白解惑
我写的相关源码:

            elseif(substr($this->getrHeader('content-type'),0,19) == 'multipart/form-data'){
                $split = '--'.\substr($this->getrHeader('content-type'),
                    strpos($this->getrHeader('content-type'),'boundary=') + 9
                );// 读取分界符
                if($split.PHP_EOL != ($f = fgets($this->socket))) {
                    echo "[  ERR  ] POST出错:分界符不符合!原分界符:$split,传递的分界符:$f\n";
                    $this->filter(400,'POST BOUNDARY DOES NOT MATCH!');
                    return;
                }
                $len = strlen($f);        // 统计最终数据
                // 保存文件到缓冲区
                $tmp = sys_get_temp_dir();          // 使用系统暂存区,可以更改
                $maxLen = (int)$this->getrHeader('content-length');// 最终数据大小
                if(!_q) echo "\n开始接收POST文件 总大小:$maxLen\n";
                $start = time();
                if(!_q) $proc = new cliProcess();
                for ($i = 0 ; $i < 8 ; $i ++) {     // 最大允许同时上传8个文件
                    if($len >= $maxLen) break;

                    $tmp = $this->parserHeader(false);// 由于文件信息与请求头很像所有直接调用
                    $len += $tmp['_length'];        // 这里卡了很久,忘记这些头也有长度
                    $file = \fopen(                 // 打开文件,其实说实在的保存到RAM也一样
                        $tmp['savePath'] = \md5(\rand(10,1000)) // 随机文件名
                    ,'w');
                    $data = '';
                    while(true) { // 未到分界处
                        $data = \fgets($this->socket);              // 每次读取32K数据
                        $len += strlen($data);                      // 统计读取的数据
                        if($split.PHP_EOL == $data) break;
                        fwrite($file,$data);           // 循环写到文件
                        if(!_q) $proc->set($len/$maxLen);// 设置进度条,这会消耗CPU,不过很少用到
                    }
                    fclose($file);
                    // 到了分界处,保存文件
                    array_push($this->file,$tmp);       // 保存到files
                }
                echo "\n接收完毕,总文件数$i,耗时".time() - $start." MS\n";
            } 

        /**
         * 解析当前请求的请求头,如第一个参数是false将不保存到缓存
         * 
         * @param ?bool $save
         */
        public function parserHeader(bool $save = true):array {
            $tmp = ['parsed'=>true];
            $len = 0;
            while(true) {
                if(($str = fgets($this->socket)) == PHP_EOL) break;
                $len += strlen($len);
                $str = trim($str);
                @list($name,$value) = \explode(':',$str,2);
                $tmp [ \strtolower($name) ] = trim($value);

            }
            $tmp['_length'] = $len;
            if($save === true) $this->requestHeader = $tmp;
            return $tmp;
        }
679 1 1
1个回答

walkor

根据手册 https://www.php.net/manual/zh/function.fgets.php
最好不用fgets读取文件数据,fgets是获取一行数据,一行数据是以换行符为边界。如果文件最后一个字符不是换行符,fgets获取最后一段文件数据时就会卡住,直到连接断开才能返回

  • helloio 2023-01-26

    那请问该怎么实现上传呢?

  • walkor 2023-01-28

    不管是get post 还是上传,都是读取socket数据,数据读取上没有区别。主要是解析http协议内容
    http上传rfc文档 https://www.ietf.org/rfc/rfc1867.txt
    最简单的方法,抓一个上传的http报文,参考协议格式从报文里截取文件内容就行了。

  • 肥臣 2023-01-28

    怎么没有“点赞”功能?

年代过于久远,无法发表回答
🔝