在无服务器环境中运行应用程序时,您可能无法将文件永久存储在本地文件系统中,因为您永远无法确定在后续请求中是否会使用相同的无服务器“容器”。所有文件都应存储在云存储系统中,例如 AWS S3,或通过 AWS EFS 存储在共享文件系统中。
为了确保应用程序环境有一个地方可以存储文件上传,您可以在环境的 vapor.yml
配置中添加一个 storage
键。此值应为有效的 S3 存储桶名称。在部署期间,Vapor 将确保此存储桶存在。如果存储桶不存在,Vapor 将创建并配置它。请记住,存储桶名称在所有 AWS 中必须是唯一的。
id: 3
name: vapor-app
environments:
production:
storage: my-bucket-name
memory: 1024
build:
- 'composer install --no-dev'
deploy:
- 'php artisan migrate --force'
要在 Lambda 上挂载文件系统,您需要 将您的环境附加到 Vapor 网络,然后使用 AWS 控制台创建一个新的弹性文件系统 (EFS)。在 EFS 仪表板中,您应该单击“创建文件系统”按钮并选择由您的 Vapor 网络创建的 VPC。创建文件系统后,单击其名称以访问配置屏幕,然后单击“访问点”。创建一个访问点,并为“POSIX 用户”和“根目录创建权限”设置的“POSIX 用户”和“组”指定 1001
。对于“根目录路径”配置选项,您应该分配一个唯一的名称,例如 /{project_name}_{environment_name}
。
创建磁盘后,您应该导航到 AWS Lambda 仪表板。进入 Lambda 仪表板后,找到您项目环境的三个 Lambda 函数(主函数、“cli”函数和“queue”函数)中的每一个,并附加您刚刚创建的文件系统。您可以通过查看 Lambda 函数的详细信息页面并单击“添加文件系统”按钮来附加文件系统。您应该将文件系统挂载到 /mnt/local
。
挂载后,您可以将文件存储到 /mnt/local
磁盘路径并从该路径检索文件。此路径将由所有三个 Lambda 函数共享并可访问。
由于 AWS Lambda 的限制,直接上传到应用程序后端的上传文件大小只能达到大约 4.5 MB。这是 AWS 强制执行的硬性限制,更新 php.ini
配置文件或任何其他配置都不会提高此限制。因此,为了确保您的应用程序用户不会收到 HTTP 413 Payload Too Large
响应,您可以在启动文件上传到应用程序后端之前使用 JavaScript 验证文件上传大小。
如果您的应用程序需要接收大于 AWS 允许的文件上传,则这些文件必须从应用程序的前端(浏览器)直接流式传输到 S3。为了帮助您,我们编写了一个 NPM 包,可以轻松地从应用程序的前端执行文件上传。
要开始使用,请安装 laravel-vapor
NPM 包
npm install --save-dev laravel-vapor
接下来,在您的应用程序的app.js
文件中,初始化全局 Vapor JavaScript 对象
// Vite
import Vapor from 'laravel-vapor'
window.Vapor = Vapor
// Mix
window.Vapor = require('laravel-vapor');
在直接将文件上传到 S3 之前,Vapor 的内部签名存储 URL 生成器将对当前已认证的用户执行授权检查。如果您还没有,您应该使用以下命令为您的应用程序创建一个UserPolicy
php artisan make:policy UserPolicy --model=User
接下来,您应该在此策略中添加一个uploadFiles
方法。此方法应在给定的已认证用户被允许上传文件时返回true
。否则,您应该返回false
/**
* Determine whether the user can upload files.
*
* @param \App\User $user
* @return mixed
*/
public function uploadFiles(User $user)
{
return true;
}
您可以在前端代码中使用Vapor.store
方法将文件直接上传到附加到您的环境的 S3 存储桶。以下示例演示了使用 Vue 的此功能
<input type="file" id="file" ref="file">
Vapor.store(this.$refs.file.files[0], {
progress: progress => {
this.uploadProgress = Math.round(progress * 100);
}
}).then(response => {
axios.post('/api/profile-photo', {
uuid: response.uuid,
key: response.key,
bucket: response.bucket,
name: this.$refs.file.files[0].name,
content_type: this.$refs.file.files[0].type,
})
});
所有上传的文件都将放置在存储桶中的tmp
目录中。此目录自动配置为清除任何超过 24 小时的文件。此功能方便地清理了已启动但未完成的文件上传,例如用户开始更新其个人资料照片但未保存更改。
默认情况下,tmp
目录是私有的。要为给定文件覆盖此设置,您可以将visibility
属性添加到提供给Vapor.store
方法的选项中。visibility
属性应分配给S3 的预定义权限授予之一
Vapor.store(this.$refs.file.files[0], {
visibility: 'public-read'
}).then(response => {
// ...
});
所有上传的文件都将使用 UUID 作为文件名进行存储。提供给store
方法的then
回调的response
将包含文件的 UUID、文件的完整 S3 密钥和文件的存储桶。然后,您可以将此信息 POST 到您的应用程序的后端以通过将其移出存储桶的tmp
目录来永久存储文件。此外,您可能希望在应用程序的数据库中存储有关文件的其他信息,例如其原始名称和内容类型
use Illuminate\Support\Facades\Storage;
Storage::copy(
$request->input('key'),
str_replace('tmp/', '', $request->input('key'))
);
在本地开发时,Vapor.store
将上传到由AWS_BUCKET
环境变量指定的存储桶。此外,您的存储桶可能需要 CORS 配置才能允许从 localhost 上传
[
{
"AllowedHeaders":[
"*"
],
"AllowedMethods":[
"GET",
"PUT"
],
"AllowedOrigins":[
"*"
],
"ExposeHeaders":[]
}
]
您的应用程序可能会在 /tmp
目录中存储临时文件。默认情况下,此目录的大小固定为 512 MB,并且其中的信息在每个请求、CLI 命令或队列作业的生命周期内都会被保留。您可以使用环境的 vapor.yml
配置文件中的 tmp-storage
、cli-tmp-storage
和 queue-tmp-storage
选项来增加或减少配置的临时存储大小。这些配置选项接受 512 MB 到 10,240 MB 之间的数值。
id: 2
name: vapor-laravel-app
environments:
production:
tmp-storage: 1024
cli-tmp-storage: 512
queue-tmp-storage: 10240
build:
- 'composer install --no-dev'