Laravel 5:如何将本地文件复制到 Amazon S3?

Posted

技术标签:

【中文标题】Laravel 5:如何将本地文件复制到 Amazon S3?【英文标题】:Laravel 5: How do you copy a local file to Amazon S3? 【发布时间】:2015-06-14 04:13:44 【问题描述】:

我正在 Laravel 5 中编写代码来定期备份 mysql 数据库。到目前为止,我的代码如下所示:

    $filename = 'database_backup_'.date('G_a_m_d_y').'.sql';
    $destination = storage_path() . '/backups/';

    $database = \Config::get('database.connections.mysql.database');
    $username = \Config::get('database.connections.mysql.username');
    $password = \Config::get('database.connections.mysql.password');

    $sql = "mysqldump $database --password=$password --user=$username --single-transaction >$destination" . $filename;

    $result = exec($sql, $output); // TODO: check $result

    // Copy database dump to S3

    $disk = \Storage::disk('s3');

    // ????????????????????????????????
    //  What goes here?
    // ????????????????????????????????

我在网上看到了建议我执行以下操作的解决方案:

$disk->put('my/bucket/' . $filename, file_get_contents($destination . $filename));

但是,对于大文件,使用 file_get_contents() 不是很浪费吗?有没有更好的解决方案?

【问题讨论】:

这是一个很好的问题,也是我现在的目标。我现在将研究tuts.codingo.me/laravel-backup-amazon-s3(看起来很有希望)以及来自@user4603841 的以下建议。 【参考方案1】:

查看文档的唯一方法是使用需要文件内容的方法put。没有在 2 个文件系统之间复制文件的方法,因此您提供的解决方案可能是目前唯一的一个。

仔细想想,最后在从本地文件系统复制文件到s3时,需要有文件内容才能放到S3中,所以在我看来确实没那么浪费。

【讨论】:

谢谢马尔辛。我觉得这可能很浪费,因为 file_get_contents 会在将整个文件发送到 S3 之前将其拉入内存。我希望有一个解决方案,文件可以从本地文件流式传输到 S3。如果您的文件只有 1 或 2 兆,这没什么大不了的,但我可以看到较大文件的内存耗尽。所有这些都是我的猜测,所以请持保留态度。 我相信有一种方法可以通过 Laravel 5 上的 Flysystem 使用流来实现。可能值得向 Frank de Jonge 询问如何做到这一点(Flysystem 的作者)。基本上,您通过流打开文件,将内容拉入并同时将该内容推送到 S3 上的文件。这样就不必将整个文件加载到内存中,这有很多原因。 @MarcinNabiałek 我并不是要侮辱你,如果那是不礼貌的行为,我深表歉意。我更新了“接受的答案”,以便看到此帖子的开发人员关注 itod 的答案,这可能是现在最好的方法。 @clone45 是的,我明白了,但在询问时可能还没有 Laravel 5.8,而且这种方法不起作用。如果在 Laravel 15 中它会被更改并且有人回答,那么您将再次将接受的答案更改为这个? @MarcinNabiałek 如果您想讨论这个问题,请随时通过我的 clone45 gmail 地址给我发送电子邮件。我在网上看到双方的争论。【参考方案2】:

您始终可以通过执行以下操作使用文件资源来流式传输文件(建议用于大文件):

Storage::disk('s3')->put('my/bucket/' . $filename, fopen('path/to/local/file', 'r+'));

另一个建议是proposed here。它使用 Laravel 的 Storage 门面来读取流。基本思路是这样的:

    $inputStream = Storage::disk('local')->getDriver()->readStream('/path/to/file');
    $destination = Storage::disk('s3')->getDriver()->getAdapter()->getPathPrefix().'/my/bucket/';
    Storage::disk('s3')->getDriver()->putStream($destination, $inputStream);

【讨论】:

我只得到“遇到格式不正确的数值”。还没找到解决办法【参考方案3】:

你可以试试这个代码

$contents = Storage::get($file);
Storage::disk('s3')->put($newfile,$contents);

作为 Laravel 文档,这是我发现在两个磁盘之间复制数据的简单方法

【讨论】:

【参考方案4】:

有一种方法可以复制文件而无需将文件内容加载到内存中。

您还需要导入以下内容:

use League\Flysystem\MountManager;

现在你可以像这样复制文件了:

$mountManager = new MountManager([
    's3' => \Storage::disk('s3')->getDriver(),
    'local' => \Storage::disk('local')->getDriver(),
]);
$mountManager->copy('s3://path/to/file.txt', 'local://path/to/output/file.txt');

【讨论】:

【参考方案5】:

我通过以下方式解决了它:

$contents = \File::get($destination);
\Storage::disk('s3')
    ->put($s3Destination,$contents);

有时我们无法使用$contents = Storage::get($file); - storage 函数获取数据,因此我们必须使用 Laravel File 提供数据的根路径,而不是使用 Storage 的存储路径>.

【讨论】:

【参考方案6】:

Laravel 现在有 putFileputFileAs 方法来允许文件流。

自动流式传输

如果你希望 Laravel 自动管理 将给定文件流式传输到您的存储位置,您可以使用 putFile 或 putFileAs 方法。此方法接受 Illuminate\Http\File 或 Illuminate\Http\UploadedFile 实例并将 自动将文件流式传输到您想要的位置:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;

// Automatically generate a unique ID for file name...
Storage::putFile('photos', new File('/path/to/photo'));

// Manually specify a file name...
Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

文档链接:https://laravel.com/docs/5.8/filesystem(自动流式传输)

希望对你有帮助

【讨论】:

以上是关于Laravel 5:如何将本地文件复制到 Amazon S3?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Laravel 5.5 应用部署到 Godaddy cPanel 共享主机

如何在 laravel 5.7 中删除用户? [复制]

如何在 laravel 5 中使用 Storage 门面获取文件 URL?

如何将本地文件复制到远程服务器

如何将FTP中的一个文件夹同步到本地

如何使用 Chef 在本地移动/复制文件