基于 Laravel 6、 Guzzle 6 下载远程文件至服务器磁盘的实现

1、现在正在尝试将远程的 ZIP 文件下载至服务器。 远程文件网址:https://www.shuijingwanwq.com/wp-content/uploads/2021/07/两位老人被两只狗给撞了.wmv

2、参考:https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html#sink 。声明响应的主体部分将要保存的位置。如图1

图1

3、参考:https://www.shuijingwanwq.com/2020/11/26/4627/ 。传入 fopen() 返回的资源将响应写入 PHP 流

    /**
     * 下载 ZIP
     * @param string $uri 文件的绝对 URI
     * @param string $absolutePath 文件的绝对路径(下载至的目标路径)
     * @return void
     */    public function downloadZip($uri, $absolutePath) {
        // 实例化 HTTP 客户端
        $client = new Client;

        // 打开即将下载的本地文件,在该文件上打开一个流
        $resource = fopen($absolutePath, 'w');

        $client->get($uri, ['sink' => $resource]);
        // file_put_contents(storage_path() . '/logs/ZipHandler-downloadZip-absolutePath-' . microtime(true) . '-' . mt_rand()  . '.txt', print_r($response, true), FILE_APPEND | LOCK_EX);

        // 关闭一个已打开的文件指针
        fclose($resource);
    }

4、抛出异常:fopen(E:\wwwroot\object\storage/theme_downloads/2022/05/19/1652927525.7561.1655422634.zip): failed to open stream: No such file or directory

ErrorException: fopen(E:\wwwroot\object\storage/theme_downloads/2022/05/19/1652927525.7561.1655422634.zip): failed to open stream: No such file or directory in E:\wwwroot\object\Modules\ThemeStoreDB\Handlers\ZipHandler.php:20
Stack trace:
#0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'fopen(E:\\wwwroo...', 'E:\\wwwroot\\wsho...', 20, Array)
#1 E:\wwwroot\object\Modules\ThemeStoreDB\Handlers\ZipHandler.php(20): fopen('E:\\wwwroot\\wsho...', 'w')
#2 E:\wwwroot\object\Modules\ThemeStoreDB\Jobs\ThemeInstallationJob.php(61): Modules\ThemeStoreDB\Handlers\ZipHandler->downloadZip('https://www.shu...', 'E:\\wwwroot\\wsho...')
#3 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Modules\ThemeStoreDB\Jobs\ThemeInstallationJob->handle()
#4 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Util.php(37): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#5 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#6 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#7 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php(590): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#8 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php(94): Illuminate\Container\Container->call(Array)
#9 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(130): Illuminate\Bus\Dispatcher->Illuminate\Bus\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#10 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(105): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#11 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php(98): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#12 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(83): Illuminate\Bus\Dispatcher->dispatchNow(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob), false)
#13 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(130): Illuminate\Queue\CallQueuedHandler->Illuminate\Queue\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#14 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(105): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#15 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(85): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#16 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(59): Illuminate\Queue\CallQueuedHandler->dispatchThroughMiddleware(Object(Illuminate\Queue\Jobs\RedisJob), Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#17 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Jobs\Job.php(88): Illuminate\Queue\CallQueuedHandler->call(Object(Illuminate\Queue\Jobs\RedisJob), Array)
#18 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(368): Illuminate\Queue\Jobs\Job->fire()
#19 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(314): Illuminate\Queue\Worker->process('redis', Object(Illuminate\Queue\Jobs\RedisJob), Object(Illuminate\Queue\WorkerOptions))
#20 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(134): Illuminate\Queue\Worker->runJob(Object(Illuminate\Queue\Jobs\RedisJob), 'redis', Object(Illuminate\Queue\WorkerOptions))
#21 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(112): Illuminate\Queue\Worker->daemon('redis', 'default', Object(Illuminate\Queue\WorkerOptions))
#22 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(96): Illuminate\Queue\Console\WorkCommand->runWorker('redis', 'default')
#23 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Illuminate\Queue\Console\WorkCommand->handle()
#24 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Util.php(37): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#25 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#26 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#27 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php(590): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#28 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Console\Command.php(134): Illuminate\Container\Container->call(Array)
#29 E:\wwwroot\object\vendor\symfony\console\Command\Command.php(255): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#30 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Console\Command.php(121): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#31 E:\wwwroot\object\vendor\symfony\console\Application.php(1009): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#32 E:\wwwroot\object\vendor\symfony\console\Application.php(273): Symfony\Component\Console\Application->doRunCommand(Object(Illuminate\Queue\Console\WorkCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#33 E:\wwwroot\object\vendor\symfony\console\Application.php(149): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#34 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Console\Application.php(93): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#35 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(131): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#36 E:\wwwroot\object\artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#37 {main}

5、需要先确保目录是存在的。判断目录是否存在,如果不存在,则递归创建。

        // 文件的相对路径
        $directory = '/theme_downloads/' . date('Y/m/d');

        // 判断目录是否存在,如果不存在,则递归创建
        if (!file_exists(storage_path('app') . $directory)) {
            // file_put_contents(storage_path() . '/logs/ThemeInstallationJob-handle-directory-' . microtime(true) . '-' . mt_rand()  . '.txt', print_r($directory, true), FILE_APPEND | LOCK_EX);
            Storage::disk('local')->makeDirectory($directory);
        }

        $uri = 'https://www.shuijingwanwq.com/wp-content/uploads/2022/05/theme-1.zip';
        $absolutePath = storage_path('app') . $directory . '/' . microtime(true) . '.' . mt_rand() . '.zip';
        // file_put_contents(storage_path() . '/logs/ThemeInstallationJob-handle-absolutePath-' . microtime(true) . '-' . mt_rand()  . '.txt', print_r($absolutePath, true), FILE_APPEND | LOCK_EX);
        app(ZipHandler::class)->downloadZip($uri, $absolutePath);

6、下载成功。但是如果下载:.zip 的文件,提示:fclose(): supplied resource is not a valid stream resource

ErrorException: fclose(): supplied resource is not a valid stream resource in E:\wwwroot\object\Modules\ThemeStoreDB\Handlers\ZipHandler.php:27
Stack trace:
#0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'fclose(): suppl...', 'E:\\wwwroot\\wsho...', 27, Array)
#1 E:\wwwroot\object\Modules\ThemeStoreDB\Handlers\ZipHandler.php(27): fclose(Resource id #1962)
#2 E:\wwwroot\object\Modules\ThemeStoreDB\Jobs\ThemeInstallationJob.php(71): Modules\ThemeStoreDB\Handlers\ZipHandler->downloadZip('https://www.shu...', 'E:\\wwwroot\\wsho...')
#3 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Modules\ThemeStoreDB\Jobs\ThemeInstallationJob->handle()
#4 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Util.php(37): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#5 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#6 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#7 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php(590): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#8 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php(94): Illuminate\Container\Container->call(Array)
#9 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(130): Illuminate\Bus\Dispatcher->Illuminate\Bus\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#10 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(105): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#11 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php(98): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#12 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(83): Illuminate\Bus\Dispatcher->dispatchNow(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob), false)
#13 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(130): Illuminate\Queue\CallQueuedHandler->Illuminate\Queue\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#14 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(105): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#15 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(85): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#16 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(59): Illuminate\Queue\CallQueuedHandler->dispatchThroughMiddleware(Object(Illuminate\Queue\Jobs\RedisJob), Object(Modules\ThemeStoreDB\Jobs\ThemeInstallationJob))
#17 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Jobs\Job.php(88): Illuminate\Queue\CallQueuedHandler->call(Object(Illuminate\Queue\Jobs\RedisJob), Array)
#18 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(368): Illuminate\Queue\Jobs\Job->fire()
#19 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(314): Illuminate\Queue\Worker->process('redis', Object(Illuminate\Queue\Jobs\RedisJob), Object(Illuminate\Queue\WorkerOptions))
#20 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(134): Illuminate\Queue\Worker->runJob(Object(Illuminate\Queue\Jobs\RedisJob), 'redis', Object(Illuminate\Queue\WorkerOptions))
#21 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(112): Illuminate\Queue\Worker->daemon('redis', 'default', Object(Illuminate\Queue\WorkerOptions))
#22 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(96): Illuminate\Queue\Console\WorkCommand->runWorker('redis', 'default')
#23 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Illuminate\Queue\Console\WorkCommand->handle()
#24 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Util.php(37): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#25 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#26 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#27 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\Container.php(590): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#28 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Console\Command.php(134): Illuminate\Container\Container->call(Array)
#29 E:\wwwroot\object\vendor\symfony\console\Command\Command.php(255): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#30 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Console\Command.php(121): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#31 E:\wwwroot\object\vendor\symfony\console\Application.php(1009): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#32 E:\wwwroot\object\vendor\symfony\console\Application.php(273): Symfony\Component\Console\Application->doRunCommand(Object(Illuminate\Queue\Console\WorkCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#33 E:\wwwroot\object\vendor\symfony\console\Application.php(149): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#34 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Console\Application.php(93): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#35 E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(131): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#36 E:\wwwroot\object\artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#37 {main}

7、在尝试关闭资源之前检查资源是否仍然是资源:

        // 检测变量是否为资源类型,如果是,则关闭
        if (is_resource($resource)) {
            fclose($resource);
        }

8、最后自行在 Windows 10 中生成一个 zip 文件,再次测试下载,下载成功。如图2

图2

永夜