在 foreach 循环中使用 file_put_contents 只会下载最后一项

Posted

技术标签:

【中文标题】在 foreach 循环中使用 file_put_contents 只会下载最后一项【英文标题】:Using file_put_contents inside of a foreach loop only downloads the last item 【发布时间】:2021-12-22 14:42:15 【问题描述】:

我正在尝试通过循环下载大量 mp4 文件并使用 file_put_contents() 保存到目录。问题是只下载了视频列表中的最后一项。

这是我的代码:

<?php
$i = 0;
foreach ($response['videos'] as $row)
    $i++;
    if($row['status'] != 'failed') 
        $videoId= '';
        $videoName = '';
        $videoId = $row['key'];
        $videoName = $row['title'];
        $filename = '';
        $filename = str_replace(' ','-',$videoName); // remove spaces from filename created by name
        

        // Initialize a file URL to the variable
        $url = "";
        $url = "http://content.jwplatform.com/videos/$videoId.mp4";
          
        // Use file_get_contents() function to get the file
        // from url and use file_put_contents() function to
        // save the file
        if (file_put_contents("Videos/".$filename.$i.".mp4", file_get_contents($url)))
        
            echo "File downloaded successfully.";
            //sleep(5);
        
        else
        
            echo "File downloading failed.";
        
        
    

?>

我尝试使用 CURL 函数而不是 file_put_contents() 来执行此操作,它成功地将所有文件放入了我的 Videos 目录,但它们都是空文件。我相信它们是空的,因为这些 mp4 URL 是安全视频,因此当您在浏览器中打开它们时,它们实际上会将您带到不同的安全 URL 以查看和下载视频。 CURL 函数无法成功获取文件数据,但似乎 file_get_contents() 确实成功获取了它(虽然只有最后一项)。

在我上面的代码中,我相信正在发生的事情是循环中的变量被一遍又一遍地覆盖,直到它到达最后一项,然后它执行 file_put_contents() 函数。如果是这种情况,我如何确保它在每个循环上执行该函数,以便下载所有文件?

修改: 这是 var_export($response['videos']) 的一些输出

array ( 0 => array ( 'key' => 'eewww123', 'title' => 'Video Name Example 1', 'description' => NULL, 'date' => 1604004019, 'updated' => 1640011490, 'expires_date' => NULL, 'tags' => NULL, 'link' => NULL, 'author' => NULL, 'size' => '240721720', 'duration' => '229.79', 'md5' => 'f0023423423423423423', 'views' => 0, 'status' => 'ready', 'error' => NULL, 'mediatype' => 'video', 'sourcetype' => 'file', 'sourceurl' => NULL, 'sourceformat' => NULL, 'upload_session_id' => NULL, 'custom' => array ( ), ), 1 => array ( 'key' => 'rr33445', 'title' => 'Another Video Name Example 1', 'description' => '', 'date' => 1594316349, 'updated' => 1640011493, 'expires_date' => NULL, 'tags' => NULL, 'link' => '', 'author' => NULL, 'size' => '525702235', 'duration' => '840.90', 'md5' => '0044455sfsdgsdfs3245', 'views' => 0, 'status' => 'ready', 'error' => NULL, 'mediatype' => 'video', 'sourcetype' => 'file', 'sourceurl' => NULL, 'sourceformat' => NULL, 'upload_session_id' => NULL, 'custom' => array ( ), ), )

所有行都没有失败状态,总共大约有 30 行,但我还有一些其他视频列表要下载,有 900 多行。

我启用了错误报告,我看到了

致命错误:允许的内存大小为 268435456 字节已用尽(已尝试 分配132120608字节)

在我的file_put_contents() 函数所在的行上。

这是我使用的 CURL 函数,它成功下载了所有文件名,但所有文件都是空的:

    function multiple_download(array $urls, $save_path = 'Videos') 
    $multi_handle = curl_multi_init();
    $file_pointers = [];
    $curl_handles = [];

    // Add curl multi handles, one per file we don't already have
    foreach ($urls as $key => $url) 
        $file = $save_path . '/' . basename($url);
        if(!is_file($file)) 
            $curl_handles[$key] = curl_init($url);
            $file_pointers[$key] = fopen($file, "w");
            curl_setopt($curl_handles[$key], CURLOPT_FILE, $file_pointers[$key]);
            curl_setopt($curl_handles[$key], CURLOPT_HEADER, 0);
            curl_setopt($curl_handles[$key], CURLOPT_CONNECTTIMEOUT, 60);
            curl_multi_add_handle($multi_handle,$curl_handles[$key]);
        
    

    // Download the files
    do 
        curl_multi_exec($multi_handle,$running);
     while ($running > 0);

    // Free up objects
    foreach ($urls as $key => $url) 
        curl_multi_remove_handle($multi_handle, $curl_handles[$key]);
        curl_close($curl_handles[$key]);
        fclose ($file_pointers[$key]);
    
    curl_multi_close($multi_handle);




multiple_download($videoURLS);

$videoURLs 是我使用上面的第一个 PHP 函数构建的包含所有唯一 URL 的数组(另一部分已注释掉)。

【问题讨论】:

你确定文件名是正确的并且每次都改变吗?如果您只能保存最后一项,那么您似乎总是会覆盖文件,因为名称不会改变;例如,尝试使用计数器命名文件以查看发生了什么 file_put_contents 在你的循环中运行,所以它应该每次都写入一个新文件。如上所述,唯一的问题可能是视频名称是否每次都相同。如果有可能,最好在尝试保存之前为每个文件名添加一些独特的内容。显然,我们目前无法看到$response['videos'] 的内容(样本),因此我们无法真正看到究竟会发生什么。 您是否拥有批量下载和存储该内容的合法许可? 不,请将解决方案作为答案发布在下面,这就是 *** 的工作原理! file_put_contents and file_get_contents exhaust memory size可能重复 【参考方案1】:

事实证明,问题在于 file_get_contents 耗尽了内存大小。从这个post,我使用了以下函数

function custom_put_contents($source_url='',$local_path='')

    $time_limit = ini_get('max_execution_time');
    $memory_limit = ini_get('memory_limit');

    set_time_limit(0);
    ini_set('memory_limit', '-1');      

    $remote_contents=file_get_contents($source_url);
    $response=file_put_contents($local_path, $remote_contents);

    set_time_limit($time_limit);
    ini_set('memory_limit', $memory_limit); 

    return $response;

这有效地将内存设置为无限,以便可以检索文件,然后在完成后将内存恢复到原始状态。有了这个功能,我就可以下载文件了。

【讨论】:

您是否尝试使用copy() 来消除内存问题?看到这个答案:***.com/a/1372144/4630325 呃希望我先尝试一下,但至少我学到了一两件事。复制确实工作得很好,而且更容易。【参考方案2】:

您必须使用标志来附加到文件而不是覆盖。

参见文档https://www.php.net/manual/fr/function.file-put-contents.php 标记 FILE_APPEND

编辑:如果所有文件都具有相同的名称,则可能会覆盖它们。您必须在循环中提供不同的名称。

foreach ($response['videos'] as $key => $row) 
    ...
    if (file_put_contents("Videos/" . $filename .$key ".mp4", file_get_contents($url))) 
   ...

在文件名中使用循环的 $key 使其成为唯一且不会被覆盖

【讨论】:

我试过这样做,但每次重新加载浏览器时,它只会使相同的 mp4 文件大小翻倍。它对仅下载最后一个文件的问题没有帮助。 所以所有其他文件都是空的?还是它们不是被创造出来的? 它们不是被创建的。只有最后一个文件被创建并且它不是空的,它可以工作。仅当我使用上面未显示的完全不同的函数 (CURL) 时,它们才全部创建/为空。 我编辑了我的答案

以上是关于在 foreach 循环中使用 file_put_contents 只会下载最后一项的主要内容,如果未能解决你的问题,请参考以下文章

何时使用 forEach(_:) 而不是 for in?

PHP - 在foreach循环中取消设置数组元素[重复]

如何使用foreach循环进入另一个foreach循环来遍历c​​odeigniter中的两个不同的数据表?

linq / lambda 中的多行 foreach 循环

在 foreach 循环中获取 ACF 字段数据 - wordpress

如何在 forEach 循环节点中使用 Promise?