# Access system env in separate cell so Livebook can offer to use secrets.Application.put_env(:demo,:s3_config,access_key_id:System.fetch_env!("LB_AWS_ACCESS_KEY_ID"),secret_access_key:System.fetch_env!("LB_AWS_SECRET_ACCESS_KEY"),endpoint_url:System.get_env("LB_AWS_ENDPOINT_URL_S3"),bucket:System.fetch_env!("LB_BUCKET_NAME"))
Playground
# Direct to S3 uploads using Phoenix Playground and ReqS3## Set BUCKET_NAME, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (and optionally AWS_ENDPOINT_URL_S3).## Based on https://hexdocs.pm/phoenix_live_view/uploads-external.html#direct-to-s3defmoduleDemoLivedousePhoenix.LiveView@impltruedefmount(_params,_session,socket)do{:ok,socket|>assign(:s3_config,Application.get_env(:demo,:s3_config))|>allow_upload(:photo,accept:~w[.png .jpeg .jpg],max_entries:1,auto_upload:true,external:&presign_upload/2)}end@impltruedefrender(assigns)do~H"""
<form id="upload-form" phx-change="validate">
<.live_file_input upload={@uploads.photo} />
</form>
<div phx-drop-target={@uploads.photo.ref} style="width: 20em; height: 10em; margin-top: 0.5em; padding: 1em; border: 1px dashed">
<div :for={entry <- @uploads.photo.entries}>
<.live_img_preview entry={entry} height="120" />
<div><%= entry.progress %>%</div>
<.link :if={entry.done?} href={presign_url(entry, @s3_config)}>Uploaded</.link>
</div>
</div>
<script type="text/javascript">
window.uploaders.S3 = function(entries, onViewError) {
entries.forEach(entry => {
let formData = new FormData()
let {url, fields} = entry.meta
Object.entries(fields).forEach(([key, val]) => formData.append(key, val))
formData.append("file", entry.file)
let xhr = new XMLHttpRequest()
onViewError(() => xhr.abort())
xhr.onload = () => xhr.status === 204 ? entry.progress(100) : entry.error()
xhr.onerror = () => entry.error()
xhr.upload.addEventListener("progress", (event) => {
if (event.lengthComputable) {
let percent = Math.round((event.loaded / event.total) * 100)
if (percent < 100) { entry.progress(percent) }
}
})
xhr.open("POST", url, true)
xhr.send(formData)
})
}
</script>
<style type="text/css">
body { padding: 1em; }
</style>
"""end@impltruedefhandle_event("validate",_params,socket)do{:noreply,socket}enddefppresign_upload(entry,socket)dos3_options=s3_options(entry,socket.assigns.s3_config)form=ReqS3.presign_form([content_type:entry.client_type]++s3_options)meta=%{uploader:"S3",key:s3_options[:key],url:form.url,fields:Map.new(form.fields)}{:ok,meta,socket}enddefppresign_url(entry,config)doReqS3.presign_url(s3_options(entry,config))enddefps3_options(entry,config)do[key:"uploads/#{entry.client_name}"]++configendendPhoenixPlayground.start(live:DemoLive)