【问题标题】:Elixir/Phoenix: Client-side / browser AJAX upload to S3Elixir/Phoenix:客户端/浏览器 AJAX 上传到 S3
【发布时间】:2019-08-21 19:59:50
【问题描述】:

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

【问题讨论】:

    标签: ajax amazon-s3 elixir phoenix-framework


    【解决方案1】:

    我最终解决了这个问题。 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 安全性,请为您的网站可访问的每个来源指定 &lt;AllowedOrigin&gt; 语句,或者将 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.
        }
      });
    });
    

    这应该让基础工作正常!

    【讨论】:

    【解决方案2】:

    基本上你需要阅读this 教程。 你使用的只是Arc,而在后台它使用ex_aws。您必须在配置文件中添加 s3 的配置,并使用 Arc 调整您的 JS。

    如果您不想将这些文件存储在数据库中,请直接在代码中使用name_of_your_uploader.store。如果你想用 AJAX 来做,你可以用频道来做。

    【讨论】:

    • 感谢分享!我熟悉那篇文章,并且已经完成了 Arc / ExAws 设置过程;正如问题标题所说,我的问题是关于设置 client-side 直接上传到 S3,这样 Phoenix 服务器就不必代理每个上传,这会阻止页面请求(至少在 Rails 世界中它会)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 1970-01-01
    • 2021-06-30
    • 2016-06-15
    • 2017-01-30
    • 2015-10-31
    • 2013-09-06
    相关资源
    最近更新 更多