PHP实现base64编码文件上传出现问题详解

  

  

领导:小同学,我们要做一个样本上传进行分析的功能,你看下是否使用base64编码加进去,这样客户端的同学就不需要用格式方式来上传了,直接使用json格式就可以上报,可以让格式上报统一。

  

小A:好的,领导,马上搞定!

  

咋看上面的对话没啥问题,很多公司团队内部为了一些标准化的问题,都会进行一些技术选型问题,但是噩梦也就从这个对话开始,功能实现当然都是很简单的,先来看简单流程图:

  

 PHP实现base64编码文件上传出现问题详解

  

本身的流程是一个很简单的文件转换成base64上传,再服务端解码保存,在开发联调过程中没有问题,非常完美的走下去了。

  

  

突然有一天终端同学误操作将一个37米文件上传,nginx与php-fpm文件上传限制均为(60米),但是在界面出现500错误,进入码头工人日志查看有一条数据:

  

允许耗尽内存大小为8388608字节(试图分配1298358字节)

  

玩php的基本都知道这是啥意思,就是代码运行过程中使用内存超过我们php。ini设置的memory_limit的值,然后就屁颠屁颠进入php . ini中找参数配置,很快找到:

  memory_limit

=128   

然后就转念一想,不应该出现这个问题,我们知道,php的内部变量使用牛(写时复制)机制来实现,那么内存申请只有在变量赋值变更才会进行

  

  

接下来我们单独写一个程序来进行测试,将一个4.89文件进行base64_encode编码与base64_decode解码,查看各自占用内存以及过程中占用峰值内存

        & lt; & # 63; php   中期美元=memory_get_usage ();   $ apk_content=file_get_contents (__DIR__。/4 bc1c8a05b8505662be778b6dad23b55.apk);   var_dump(“文件加载到内存:”。轮((memory_get_usage()——中期美元)/1024/1024,2)。“M”);   var_dump(的过程中峰值使用的内存:”。轮(memory_get_peak_usage ()/1024/1024, 2)。“M”);      设置(中期美元);   中期美元=memory_get_usage ();   (base64_encode=base64_encode美元apk_content);设置($ apk_content);   var_dump (“base64_encode占用内存:“。轮((memory_get_usage()——中期美元)/1024/1024,2)。“M”);   var_dump(的过程中峰值使用的内存:”。轮(memory_get_peak_usage ()/1024/1024, 2)。“M”);      设置(中期美元);   中期美元=memory_get_usage ();   base64_decode ($ base64_encode);   var_dump (“base64_decode占用内存:“。轮((memory_get_usage()——中期美元)/1024/1024,2)。“M”);   var_dump(的过程中峰值使用的内存:”。轮(memory_get_peak_usage ()/1024/1024, 2)。“M”);   设置(中期美元);      

执行结果:

  
  

字符串(29)“文件加载到内存:4.89“
  字符串(38)“过程中峰值使用的内存:5.25“
  字符串(33)“base64_encode占用内存:1.63 m”
  字符串(39)“过程中峰值使用的内存:11.76“
  字符串(30)“base64_decode占用内存:0 m”
  字符串(38)“过程中峰值使用的内存:13.4 m”

     

通过上面结果可以看出

  
      <李>加载文件使用内存没有太大问题,加载过程使用的峰值在5.25米,高出整体文件大小不的多,这在文件加载过程有一些临时申请内存的问题   <李> base64_encode占用内存,这个在使用的时候,就已经将内存差不多进行一个翻倍,而这基本上也是在内核解析过程中,进行了内存申请,可以理解,文件本身占用内存+ base64_encode解析后的内存,两份内存同时存在的李   <李> base64_decode操作,这个操作就是解密了,解密过程中,这里直接就占用了3倍多的内存操作,问题就出在这里,在场景中出现的问题是一个37米的文件,为什么就把单个fpm的128米内存占满了呢   
  

  

base64_encode源码解析

  

首先找到对应的c文件base64.c,找到里面php_base64_encode函数

        PHPAPI zend_string * php_base64_encode (const unsigned char * str, size_t长度)/* {{{*/{   const unsigned char *当前=str;   unsigned char * p;   zend_string *结果;      结果=zend_string_safe_alloc(((+ 2)长度/3),4 * sizeof (char), 0, 0);   p=(unsigned char *) ZSTR_VAL(结果);   ...   }      

我们先来分析这段代码,因为这里涉及到内存的问题,那么我们就看

  

结果=zend_string_safe_alloc(((+ 2)长度/3),4 * sizeof (char), 0, 0),

PHP实现base64编码文件上传出现问题详解