Stream_Copy_To_Stream() php的替代方法

7
我目前正在开发一个文件共享网站,遇到了一些小问题。我正在使用 uploadify 上传脚本,它运行得非常完美,但如果用户希望上传的文件被加密,则有一个工作代码可以实现此功能(如下所示)。但是,我的服务器只有1GB内存,并且使用 stream_copy_to_stream 看起来会占用实际文件大小的内存,而我的最大上传大小为256,因此我知道当该网站上线并且多个人同时上传大文件时,将会出现问题。基于我的下面的代码,是否有任何几乎不使用或根本不使用内存的替代选项?即使需要更长时间,我也不介意,我只需要这个功能运行。我已经可下载版本可以正常运行,因为我直接解密文件并立即通过浏览器传递,因此在下载时解密,这样效率很高,但这种上传问题看起来不太好。谢谢你的帮助。
$temp_file = $_FILES['Filedata']['tmp_name'];
    $ext = pathinfo($_FILES['Filedata']['name'], PATHINFO_EXTENSION);
    $new_file_name = md5(uniqid(rand(), true));
    $target_file = rtrim(enc_target_path, '/') . '/' . $new_file_name . '.enc.' . $ext;

    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $key = substr(md5('some_salt' . $password, true) . md5($password . 'more_salt', true), 0, 24);
    $opts = array('iv' => $iv, 'key' => $key);

    $my_file = fopen($temp_file, 'rb');

    $encrypted_file_name = $target_file;
    $encrypted_file = fopen($encrypted_file_name, 'wb');

    stream_filter_append($encrypted_file, 'mcrypt.rijndael_128', STREAM_FILTER_WRITE, $opts);
    stream_copy_to_stream($my_file, $encrypted_file);

    fclose($encrypted_file);
    fclose($my_file);
    unlink($temp_file);

temp_file是我看到的上传文件的第一个实例。


欢迎来到StackOverflow。如果有答案解决了你的问题,你可以接受这个答案。然后你也可以使用灰色向上箭头为一个或多个答案投票。 - GG.
1
感谢欢迎,很高兴知道在找到解决方案时该怎么做。 - user1556495
1个回答

8
你是否可以尝试像这样分块读取文件以获得更好的结果?:
$my_file = fopen($temp_file, 'rb');

$encrypted_file_name = $target_file;
$encrypted_file = fopen($encrypted_file_name, 'wb');

stream_filter_append($encrypted_file, 'mcrypt.rijndael_128', STREAM_FILTER_WRITE, $opts);
//stream_copy_to_stream($my_file, $encrypted_file);

rewind($my_file);

while (!feof($my_file)) {
    fwrite($encrypted_file, fread($my_file, 4096));
}

你也可以尝试在调用stream_copy_to_stream之前调用stream_set_chunk_size,以设置它在从源流复制到目标流时使用的缓冲区大小。
希望这有帮助。
编辑:我使用了这段代码,在上传一个700MB的电影文件时,PHP的峰值内存使用量为524,288字节。看起来stream_copy_to_stream将尝试将整个源文件读入内存,除非你传递长度和偏移参数以分块读取。
$encrypted_file_name = $target_file;
$encrypted_file = fopen($encrypted_file_name, 'wb');

stream_filter_append($encrypted_file, 'mcrypt.rijndael_128', STREAM_FILTER_WRITE, $opts);

$size = 16777216;  // buffer size of copy
$pos  = 0;         // initial file position

fseek($my_file, 0, SEEK_END);
$length = ftell($my_file);    // get file size

while ($pos < $length) {
    $writ = stream_copy_to_stream($my_file, $encrypted_file, $size, $pos);
    $pos += $writ;
}

fclose($encrypted_file);
fclose($my_file);
unlink($temp_file);

嗨,我找到了问题所在,我必须将fwrite文件从encrypted_file_name更改为encrypted_file,是的,这对于一个23 mb的文件上传可以只使用约13 mb的内存。我仍然担心达到我的内存限制,还有其他什么方法可以实现几乎零kb的内存占用吗? - user1556495
好的,发现了,抱歉我的错误 :) 13MB远超过你的256M的内存限制。我不确定是代码的哪一部分将其推高到13MB,但这对于处理大文件的PHP脚本来说是半合理的用法。您的内存限制也是每个实例的,它不计算同时运行的所有脚本。使用像Zend_Debugger或XDebug这样的分析器可以帮助分析内存使用情况,也许您可以看到减少一些内存使用的机会。 - drew010
1
@CristianRivera 看看我的修改,我用修改后的代码得到了不错的结果。 - drew010
2
@drew010,你的代码非常有帮助,但在while循环之前加上rewind($my_file);后才对我起作用。 - MM.
1
就我所知,我在 PHP7.3 中测试了 stream_copy_to_stream 的内存使用情况,并且它使用不到 2MB 的 RAM 就可以复制 1024 GB RAM,具体信息请参见 https://paste.debian.net/plain/1276382。 - hanshenrik
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接