Using file upload in Phoenix Liveview with Cloudflare R2

It’s a bit late for the reply, but the reason it doesn’t work is because Cloudflare R2 does not support POST via presigned url yet. Here’s the doc:

POST, which performs uploads via native HTML forms, is not currently supported.

So, instead of a POST, a PUT request can be used. You can use ExAws.S3 to generate the presigned url with PUT:

def presigned_put_upload(key) do
  ExAws.Config.new(:s3)
  |> ExAws.S3.presigned_url(:put, @bucket, key)
end

Then in your presign_upload(..):

defp presign_upload(entry, socket) do
    # ...

    {:ok, url} = presigned_put_upload(filename(entry))

    meta = %{
      uploader: "S3",
      key: filename(entry),
      url: url,
    }
    {:ok, meta, socket}
end

All this is to allow us to pass the presigned URL with PUT method to the JS side. In the JavaScript side, you’ll need to make some changes, similar to below:

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){
+      if(event.lengthComputable) {
         let percent = Math.round((event.loaded / event.total) * 100)
-        if(percent < 100){ entry.progress(percent) }
+.       # For some reason, when using PUT, we need to change `<` to `<=`, 
+        # else, the progress will stuck, never reach 100% and the live view side
+        # won't be triggered.
+        if (percent <= 100) { entry.progress(percent) } 
       }
     })

-    xhr.open("POST", url, true)
-    xhr.send(formData)
+    xhr.open("PUT", url, true)
+    xhr.send(entry.file)
   })
}

Hope it helps!

3 Likes