Bubbling errors from POST route hit from Liveview

I have a Liveview page with a form that on submit hits a POST route in a controller to generate a download based on the info selected from the form. This works fine, but I am wondering how is the best way to let the Liveview know if the download was sent successfully or not.

Controller looks like:

def download(conn, params) do
    data = do_something_with_params(params)
    conn
    |> send_download({:binary, data}, filename: "test.csv")
  end

You could achieve this by using PubSub and have the controller broadcast a message that will be received by the view.

ok cool, was hoping there was an easier way.

Maybe there is one and I just can’t see it :slight_smile:

However, using PubSub is quite easy

So, I’ve reached another problem I have a case statement like below and I can’t figure out how to keep from redirecting away from the Liveview when the :error case comes up.

case get_report do
      {:ok, csv} ->
        conn
        |> send_download({:binary, csv}, filename: "test.csv")
      {:error, _msg} ->
        do_something_pub_subby()
        conn
        |> ????
    end

currently just redirecting back to the liveview with a full reload but this doesn’t feel like the best way.

At this point, I would suggest you rethink the design of your web app so as to keep as much of your logic as possible in the liveview and only offload the file transfer to the non-liveview part of your app.

There is a similar question to which I already proposed a solution. You can check out the details here: With Liveview, what is best way to download a file to the user's browser? (redirect through Phoenix Controller and back to Liveview, or use a background Task?) - #2 by trisolaran

Summary: generate the file as a response to a liveview event. If the file generation fails, render the error in your liveview. If it succeeds, place the generated file in your application’s priv/static folder and render a download link in your liveview. The dowlonad will be handled by Plug.Static so you won’t need to write any custom controller for it. Of course, now you won’t need to use PubSub anymore :slight_smile:

This is something I implemented recently in one of my projects, and I found this approach to work well.

Yeah I like that idea. And I guess post download you could just delete the file.

I like this approach but how would you deal with ensuring that the file is completely downloaded before deleting it? I ask because looking at Plug.Static's code there seems to be no way to hook into that pipeline to know when the file is done being uploaded.

My naive approach would be setting some kind of async task to delete the file after some minutes and let the liveview know to remove the link from the page if it’s still up and the user hasn’t downloaded the file yet.

Alternatively, not show a download link but rather push an event to a hook that initiates the download for the user without them clicking on anything, that way they’d have to resubmit the form if the download fails. In that case one could also check if the file is still in the static folder from the previous request and serve it before it’s deleted, optionally resetting the deletion timer.

I had similar problem. Now I gave up the regular controller download, and just chunk/base64 encode the data and push the chunks via push_event to the client side. It is not the best way to utilize the bandwidth, but everything is contained in the liveview. The liveview always know the current state of affairs and there is no garbage to cleanup.

fair enough, but what if the user needs a file?

Of course. you can use a blob url once the file is fully transferred to the client side.

1 Like

This solution requires writing considerable client side JS. But the biggest problem is that putting a file in the url will limit your file size to a few KB

~20 lines, I’ve done this. Blob size is limited by memory, not by url length. It can go up to hundreds of MB even on a phone.

Ah! My bad, I thought these blob URLs where a form of data URLs. Now I see that it’s a completely different mechanism without the limitations of data URLs. Very interesting, I didn’t know that something like that existed. So yeah, now I understand your solution, and I like it