Agency Docs
QBESXStandalone

Agency-Phone 媒体存储

现已搭载 AgencyOS27:已拥有手机的用户可免费更新。一款适用于 FiveM 的现代手机,支持 QBCore、ESX 或独立运行,并能自动识别框架。应用涵盖基于 pma-voice 通话的电话、Buzz 消息、FaceZoom.

v27.1.1付费8 页面

照片、视频和语音消息的存储位置

从 27.1.1 版本起,Agency Phone 的每一张照片、每一段视频、每一次屏幕录制和每一条语音消息都由脚本自己保存。存储位置由你决定:

存储方式设置适用场景
本地(默认)无需设置。安装后即可使用。所有服务器。文件保存在你自己的服务器上,存放在你的数据库中。
Cloudflare R2只需设置一次,大约 10 分钟。10 GB 以内免费。视频很多或玩家人数非常多的服务器。

Fivemanage 和 Discord 不再用作存储。Discord webhook 仍可用于日志。

本地存储(默认)

无需任何设置。脚本在首次启动时会在你的数据库中创建两张表(agency_media 和 agency_media_parts),并把文件保存在其中。你只需要 oxmysql,而脚本本来就需要它。

这对你意味着:

  • 无需账号,无需 API 密钥,无需额外的资源。没有任何内容在你的服务器之外运行。
  • 更新很安全。文件保存在数据库中,而不是资源文件夹里,所以更新时替换文件夹不会丢失任何照片。
  • 你的数据库备份包含这些媒体文件。数据库的体积会比文件本身大约多出三分之一。一张照片约 150 KB。
  • Agency Phone 和 Agency Pad 共用存储。用其中一个拍的照片也会出现在另一个上,包括与 Agency Cloud 同步的照片。
  • Agency Companion 应用也会显示这些内容,支持 iOS、Android 和网页版应用。这同样无需任何设置。

速度

文件通过 FiveM 自身的连接传输,不会阻塞游戏。FiveM 限制了玩家向服务器发送数据的速度(约每秒 165 KB),因此:

内容保存大约需要
照片1 秒
语音消息不到 1 秒
15 秒的视频片段20 秒(相机会以百分比显示进度)

向其他玩家显示文件很快,一段完整的视频几秒钟就能送达。如果你的服务器录制大量视频,Cloudflare R2 几乎可以即时保存视频片段。

config.lua 中的设置

AgencyConfig.MediaStorage = {
    Provider = 'local',                -- 'local' or 'cloudflare'

    MaxFileMegabytes = 10,             -- largest single file (clips, recordings)
    TransferKilobytesPerSecond = 1000, -- 'local': how fast files are sent to players

    Local = {
        RetentionDays = 30,            -- delete files older than this (0 = keep forever)
        MaxStorageGigabytes = 10,      -- delete the oldest files above this (0 = no limit)
    },
}
设置项作用
RetentionDays超过这个天数的文件会每小时自动删除一次。被删除的照片在相册中会显示一个小占位图。设为 0 则永久保留全部文件。
MaxStorageGigabytes当所有文件的总大小超过这个值时,会优先删除最旧的文件。设为 0 表示不限制。
MaxFileMegabytes更大的文件会被拒绝。这也限制了视频片段和屏幕录制。
TransferKilobytesPerSecond向玩家发送文件时每位玩家的速度。游戏始终拥有优先权。

Cloudflare R2 分步指南

Cloudflare R2 每月提供 10 GB 存储、100 万次上传和 1000 万次下载的免费额度,而且下载永远不收费。启用 R2 时,Cloudflare 会要求你绑定付款方式,即使使用免费方案也一样。只有超出免费额度时才需要付费。

1. 启用 R2

  1. 登录 dash.cloudflare.com(免费账号即可)。
  2. 在左侧菜单中打开 Storage & databases,然后打开 R2 Object Storage。
  3. 添加 R2 订阅。概览中显示当前应付 $0.00。接受条款并点击 Activate。

2. 创建存储桶

  1. 点击 Create bucket。
  2. 为它命名,例如 myserver-media。名称之后无法更改。
  3. 位置保持 Automatic,存储类别保持 Standard。
  4. 点击 Create bucket。

3. 让文件可以被查看

  1. 打开你的存储桶,进入 Settings。
  2. 在 Public Development URL 下点击 Enable,输入 allow 并确认。
  3. 复制出现的地址,格式类似 https://pub-1a2b3c….r2.dev。这就是你的 PublicUrl。

对于访问量很大的服务器,Cloudflare 会对 r2.dev 地址进行限制。如果你在 Cloudflare 上有自己的域名,可以在同一位置的 Custom Domains 下添加它,并改用该地址作为 PublicUrl。

4. 允许从游戏中上传

  1. 仍在 Settings 中,找到 CORS Policy 并点击 Add。
  2. 清空文本框中的所有内容,粘贴下面的内容,然后点击 Save:
[
  {
    "AllowedOrigins": ["https://cfx-nui-agency-phone", "https://cfx-nui-agency-pad"],
    "AllowedMethods": ["PUT", "GET"],
    "AllowedHeaders": ["*"],
    "MaxAgeSeconds": 3600
  }
]

如果没有这一步,游戏将无法上传,照片会保存失败。如果你重命名了资源文件夹,请把你的文件夹名称写在 cfx-nui- 后面。

5. 创建密钥

  1. 返回 R2 Object Storage,点击 Manage API tokens。
  2. 点击 Create API token(账号级令牌即可)。
  3. 权限选择:Object Read & Write。
  4. 在存储桶选项下,只选择你自己的存储桶。
  5. 点击 Create。Cloudflare 只会显示一次 Access Key ID 和 Secret Access Key,请现在就把两者都复制下来。
  6. 你的 Account ID 显示在 R2 概览页,它也是 S3 地址的第一部分(https://<Account ID>.r2.cloudflarestorage.com)。

6. 在脚本中填写

密钥要填入 config_server.lua。这个文件只由服务器读取,不会发送给玩家。不要把它们放进 config.lua:那个文件会发送给每一位玩家。

-- config_server.lua
AgencyServerConfig.Cloudflare = {
    AccountId       = 'your account id',
    Bucket          = 'myserver-media',
    AccessKeyId     = 'your access key id',
    SecretAccessKey = 'your secret access key',
    PublicUrl       = 'https://pub-1a2b3c....r2.dev',
}

然后在 config.lua 中设置存储方式,并重启该资源:

AgencyConfig.MediaStorage = {
    Provider = 'cloudflare',
    ...
}

服务器控制台现在会显示 Media: storage: Cloudflare R2, bucket myserver-media。如果缺少了什么,控制台会指出缺少的内容,在问题修复之前脚本会继续保存到本地,所以相机永远不会停止工作。

同时使用 Agency Phone 和 Agency Pad?两者可以使用同一个存储桶和同一个密钥,各自把文件保存在自己的文件夹中。

检查是否正常工作

在服务器控制台中(或以管理员身份在游戏中)运行下面的命令,并使用一位在线玩家的 ID:

phonemediatest 1

该玩家的 Agency Phone 会保存一张小的测试图片,像其他玩家一样把它重新加载回来并显示,再播放一条简短的语音消息。控制台会返回:

Media test passed for player 1 in 1361 ms: upload ok, download ok, shown as picture ok, app markup and backgrounds ok, voice message plays ok

加上 big 还可以额外测试一段 6 MB 的视频片段:phonemediatest 1 big。

常见问题与解决办法

你看到的现象解决办法
控制台中出现 could not create the media tablesoxmysql 没有运行,或无法连接到数据库。请在 agency-phone 之前启动 oxmysql。
Provider is 'cloudflare', but config_server.lua is missing ...五个值中有一个为空,或者 PublicUrl 不是以 https:// 开头。请检查第 6 步。
照片保存失败,提示 Cloudflare HTTP 403该密钥对这个存储桶没有写入权限,或者存储桶名称有误。请检查第 5 步。
使用 Cloudflare 时照片保存失败,提示 Failed to fetch缺少第 4 步中的 CORS 策略,或者其中的资源名称不一致。
使用 Cloudflare 时已保存的照片不显示第 3 步中的 Public Development URL 没有启用,或者 PublicUrl 指向了另一个存储桶。
视频保存需要很长时间本地存储下这是正常现象,参见上文的速度部分。如需即时保存视频片段,请使用 Cloudflare R2。
旧照片不见了被 RetentionDays 删除了。请调大该值或设为 0。

从 Fivemanage 或 Discord 迁移

无需迁移任何数据。之前保存的照片保留原来的链接,只要对应的服务仍然提供这些文件,它们就仍然可以查看。所有新内容都会保存到你所选择的存储中。旧的设置项 MediaStorageProvider、FiveManageApiUrl、FiveManageApiKey 和 ScreenshotWebhook 会被忽略,可以从你的配置中删除。

安全

  • 只有连接到你服务器的玩家才能上传。每次上传都会先进行声明,并检查类型和大小。
  • 内容会根据开头的几个字节进行检查。网页或脚本永远无法伪装成照片被存储。
  • 文件名是 128 位随机值,无法被猜到,也无法被列出。
  • 上传和下载按玩家分别限速。
  • 使用 Cloudflare 时,每次上传都会获得一个独立的链接,有效期 5 分钟,只对应一种类型、一种大小的一个文件。密钥永远不会离开你的服务器。

看完本页仍未解决?接下来由我们的支持团队接手。