【问题标题】:How to base64-decode large files in PHP如何在 PHP 中对大文件进行 base64 解码
【发布时间】:2010-10-27 16:05:59
【问题描述】:

我的 PHP Web 应用程序有一个 API,可以接收经过 base64 编码的相当大的文件(最大 32 MB)。目标是将这些文件写入我的文件系统的某个位置。当然是解码。什么是最不占用资源的方法?

编辑:通过 API 接收文件意味着我的 PHP 应用程序中有一个 32MB 的字符串,而不是磁盘某处的 32MB 源文件。我需要将该字符串解码到文件系统中。

使用 PHP 自己的 base64_decode() 并没有减少它,因为它使用了 lot 内存,所以我一直遇到 PHP 的内存限制(我知道,我可以提高这个限制,但我没有让 PHP 每个进程使用 256MB 左右感觉很好)。

还有其他选择吗?我可以手动做吗?还是将文件编码写入磁盘并调用一些外部命令?有什么想法吗?

【问题讨论】:

    标签: php memory base64


    【解决方案1】:

    尽管这有一个可接受的答案,但我有不同的建议。

    如果您从 API 中提取数据,则不应将整个有效负载存储在变量中。使用 curl 或其他 HTTP 提取器,您可以自动将数据存储在文件中。

    假设您通过一个简单的 GET url 获取数据:

    $url = 'http://www.example.com/myfile.base64';
    $target = 'localfile.data';
    
    $rhandle = fopen($url,'r');
    stream_filter_append($rhandle, 'convert.base64-decode');
    
    $whandle = fopen($target,'w');
    
    stream_copy_to_stream($rhandle,$whandle);
    fclose($rhandle);
    fclose($whandle);
    

    好处:

    • 应该更快(减少大量变量的复制)
    • 很少的内存开销

    如果您必须从临时变量中获取数据,我可以建议这种方法:

    $data = 'your base64 data';
    $target = 'localfile.data';
    
    $whandle = fopen($target,'w');
    stream_filter_append($whandle, 'convert.base64-decode',STREAM_FILTER_WRITE);
    
    fwrite($whandle,$data);
    
    fclose($whandle);
    

    【讨论】:

    • 一个好主意,但不是我想要的。就我而言,客户端应用程序通过 XML-RPC (HTTP POST) 将大文件推送到我的服务器(以及一些其他参数)。客户端可能位于 NAT 和防火墙之后,因此无法使用 GET 从客户端获取数据。
    • 如果 xml rpc 响应的结构有些静态,您可以手动遍历响应正文,这样您仍然可以完全避免所有内存使用。如果您必须将数据放在临时变量中,您可以稍微更改设置。 (我在示例之后立即更新示例;))
    • 感谢您的更新。我发现它优于我最初接受的答案。
    • 关于您的第二个示例,如果您必须接受通过 POST 上传的文件并将其从服务器推送到肥皂服务器,这会有所不同吗?
    • file_put_contents() 期望第一个参数是一个字符串,在这种情况下传递一个资源。我在上面的代码中遇到了这样的错误
    【解决方案2】:

    以更小的块解码数据。 Base64 数据的四个字符等于“Base256”数据的三个字节。

    因此您可以将每个 1024 个字符分组并将它们解码为 768 个八位字节的二进制数据:

    $chunkSize = 1024;
    $src = fopen('base64.data', 'rb');
    $dst = fopen('binary.data', 'wb');
    while (!feof($src)) {
        fwrite($dst, base64_decode(fread($src, $chunkSize)));
    }
    fclose($dst);
    fclose($src);
    

    【讨论】:

    • 谢谢。在我将其标记为已接受之前的一件事:在我最初的问题中,我提到源文件来自 API。因此,它是 PHP 中的一个变量(一个 32 MB 的字符串),而不是您从中读取的文件。有什么东西可以代替你的 fread() 有效地返回我的字符串块吗? IE。没有制作太多占用内存的重复副本?
    • 您可以通过php://input 读取输入。见docs.php.net/manual/en/wrappers.php.php
    【解决方案3】:

    传输 32Mb 字符串不是一个好主意。但我有一个适合我的任务的解决方案,它可以接受从浏览器到应用服务器的任何大小的文件。算法: 客户

    1. Javascript:使用 FileReader 和 readAsDataURL() 从 INPUT 读取文件到 FILE var。
    2. 将 FILE 中的所有数据从开头剪切到第一个 "," 位置,将其与 array_chunks 拆分为 max_upload_size/max_post_size php var。
    3. 发送带有 UID、块编号和块数量的块并等待响应,然后一个接一个地发送另一个块。

    服务器端 将每个块写入到最后一个块。然后用流做base64:

    $src = fopen($source, 'r');
    $trg = fopen($target, 'w');
    stream_filter_append($src, 'convert.base64-decode');
    stream_copy_to_stream($src, $trg);
    fclose($src);
    fclose($trg);
    

    ...现在您的 base64 解码文件位于 $target 本地路径中。笔记!不能读写同一个文件,所以$source和$target必须不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-17
      • 2018-02-22
      • 1970-01-01
      • 2019-10-05
      • 2015-01-16
      • 1970-01-01
      相关资源
      最近更新 更多