我最终解决了这个问题。 S3 帐户需要设置,要添加的服务器端代码和要添加的客户端代码。
S3 设置
首先,您需要一个 AWS 账户和一个 S3 存储桶。创建存储桶。
在存储桶设置 -> 权限 -> 公共访问设置中,确保未选中“阻止新的公共 ACL 和上传公共对象”和“删除通过公共 ACL 授予的公共访问”。
然后在 Permissions -> CORS configuration 下,添加一个 CORS 配置以允许来自您的站点的跨域浏览器请求,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://localhost:4000</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
(如果您不关心 CORS 安全性,请为您的网站可访问的每个来源指定 <AllowedOrigin> 语句,或者将 AllowedOrigin 设置为 *。)
同时设置一个仅有权访问此 S3 存储桶以及您希望允许的特定操作的 IAM 角色也是一种良好的卫生习惯。然后在服务器设置步骤中,您提供该 IAM 角色的访问凭证,而不是您的 AWS 账户访问凭证。我在此处显示的代码中跳过了这一步。
服务器端
在mix.exs中,确保安装了ex_aws和ex_aws_s3 deps(注意版本锁定在2.0):
# in mix.exs, in the deps list
# Stay at 2.0 to avoid presigned url bug: https://github.com/ex-aws/ex_aws/issues/602
{:ex_aws, "2.0.1"},
{:ex_aws_s3, "~> 2.0"},
# required by :ex_aws
{:sweet_xml, "~> 0.6"},
在 config/config.exs 中,提供您的 AWS 访问凭证和 S3 设置:
config :ex_aws,
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
region: "us-east-1",
s3: [
scheme: "https://",
host: "s3.amazonaws.com",
region: "us-east-1" ]
(为了与 Heroku 兼容,我将敏感值存储在 config/secrets.exs 中设置的环境变量中,该变量不包含在 Git 存储库中。)
现在设置控制器操作以呈现将发生 S3 文件上传的页面。最小的控制器操作可能如下所示:
def edit(conn, %{"id" => id}) do
render conn, "edit.html", presigned_s3_url: presigned_s3_url(id)
end
defp presigned_s3_url(id) do
config = ExAws.Config.new(:s3)
bucket = System.get_env("S3_BUCKET")
path = "uploads/interview_recordings/#{id}.webm"
# Set the file permission so this file can be publicly linked
query_params = [{"x-amz-acl", "public-read"}, {"contentType", "binary/octet-stream"}]
# NOTE: Also set option `virtual_host: true` if using an EU region
options = [query_params: query_params]
{:ok, url} = ExAws.S3.presigned_url(config, :put, bucket, path, options)
url
end
此#edit 操作提供了一个变量@presigned_s3_url,它授权我们的客户端代码将文件上传到 S3。注意调用ExAws.S3.presigned_url时指定的:put方法;如果您指定不同的请求方法或省略查询参数等,S3 上传请求可能会被拒绝。您在此处指定的参数必须与实际 AJAX 请求的参数完全匹配,否则 S3 将拒绝上传,因为 S3 生成的签名与我们生成的签名不匹配。
客户端
您的设置会有所不同,因此我不会粘贴任何 HTML 标记。唯一重要的部分是:一些客户端操作(在我的情况下,录制 HTML5 视频)导致文件对浏览器 JS 可用,当客户端单击“提交”按钮时,我们向签名的 S3 发出 PUT 请求附有文件数据的 url。我的 AJAX 调用如下所示:
$('.js-submit-recording').click(function(e) {
e.preventDefault();
console.log("Uploading the recording to S3...");
$.ajax({
url: $(this).data('presigned-s3-url'), // the url we generated server-side
type: "PUT",
contentType: "binary/octet-stream",
processData: false, // Treat the data as a raw file, not as a POST form
data: recording_data, // a blob object
success: function() {
console.log("File successfully uploaded!");
// Now you can initiate next steps like making a POST request to our
// Phoenix server to record the newly uploaded file in our db
},
error: function() {
console.log("File not uploaded :-/");
console.log(arguments);
// If you get errors, open Chrome's Network tab and look at the
// response body for the S3 request. It will describe the reason /
// error and some details that can help with troubleshooting.
}
});
});
这应该让基础工作正常!