Elixir/Phoenix:客户端/浏览器AJAX上传到S3

Top*_*unt 1 ajax amazon-s3 elixir phoenix-framework

我的Elixir/Phoenix应用程序需要能够录制HTML5视频,允许用户查看他们录制的视频,然后通过AJAX将录制内容直接上传到S3进行存储,并向Elixir服务器发送POST以存储新上传的对象.在Rails世界中,有关于如何实现这一目标的各种宝石和冗长的教程; 在Elixir/Phoenix进行直接到S3文件上传的简单方法是什么?

Top*_*unt 6

我最终一点一点地解决了这个问题.S3帐户需要设置,要添加的服务器端代码和要添加的客户端代码.

S3设置

首先,您需要AWS账户和S3存储桶.创建存储桶,然后在存储桶属性 - >权限部分下,添加格式如下的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>
Run Code Online (Sandbox Code Playgroud)

此CORS规则允许跨域请求(当您对当前URL的域以外的域进行AJAX调用时与Web浏览器建立信任所需)仅针对PUT请求,并且仅针对源页面URL为的请求<AllowedOrigin>(凤凰开发服务器).您可以设置*mix.exs(允许任何来源)或您网站的域名.

设置只能访问此S3存储桶的IAM角色,并且只允许您要允许的特定操作,这是一种良好的卫生习惯.然后,在服务器设置步骤中,您将提供IAM角色的访问凭据,而不是您的AWS账户访问凭据.我在这里显示的代码中跳过了这一步.

服务器端

安装ex_awsgem和所有依赖项(poison,hackney和s​​weet_xml).

ex_aws_s3,提供您的AWS访问凭据和S3设置:

  # 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"},
Run Code Online (Sandbox Code Playgroud)

(要与Heroku兼容,我将敏感值存储在设置的环境变量中config/config.exs,从Git 仓库中排除.)

现在设置控制器操作,该操作呈现将在其上进行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" ]
Run Code Online (Sandbox Code Playgroud)

此#edit操作提供了一个变量config/secrets.exs,该变量授权我们的客户端代码将文件上载到S3.注意@presigned_s3_url调用时指定的方法:put; 如果您指定不同的请求方法或省略查询参数等,S3上传请求可能会被拒绝.您在此处指定的参数必须与实际AJAX请求的参数完全匹配,否则S3将拒绝上传,因为S3生成的签名与我们生成的签名不匹配.

客户端

您的设置会有所不同,因此我不会粘贴任何HTML标记.唯一重要的部分是:一些客户端操作(在我的情况下,记录HTML5视频)导致文件可供浏览器JS使用,当客户端点击"提交"按钮时,我们向签名的S3发出PUT请求带有附加文件数据的url.我的AJAX调用如下所示:

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
Run Code Online (Sandbox Code Playgroud)

这应该是基础工作!