I have a controller that will generate a zip file on-demand and send that to the user using Conn.chunk/2
:
defmodule CoreWeb.Export.AppointmentImagesController do
@moduledoc false
alias Core.Marketplace.Ap.AppointmentEvent
use CoreWeb, :controller
def index(conn, %{"appointment_id" => appointment_id} = _params) do
%{current_user: actor} = conn.assigns
case Ash.get(AppointmentEvent, %{id: appointment_id}, actor: actor) do
{:ok, appointment} ->
IO.puts("generating stream")
stream = image_as_zip_stream!(appointment)
IO.puts("start download")
conn =
conn
|> put_resp_content_type("application/zip")
|> put_resp_header("content-disposition", ~s[attachment; filename="#{appointment_id}.zip"])
|> send_chunked(:ok)
IO.puts("start sending chunks")
stream
|> Stream.each(fn results ->
case Plug.Conn.chunk(conn, results) do
{:ok, _} -> IO.puts("got here!!!")
{:error, error} -> dbg(error)
end
end)
|> Stream.run()
IO.puts("DONE!!!")
conn
{:error, _} ->
conn
|> put_status(500)
|> json(%{error: "Failed to generete the zip file, try again later."})
end
end
defp image_as_zip_stream!(appointment) do
appointment
|> Map.fetch!(:event_json)
|> Map.fetch!("synced")
|> Enum.with_index()
|> Enum.map(fn {%{"s3Url" => url}, index} ->
[bucket, path] = url |> URI.parse() |> Map.fetch!(:path) |> String.split("/", parts: 2, trim: true)
bucket
|> ExAws.S3.download_file(path, "#{index}.jpg")
|> ExAws.stream!()
|> then(& Zstream.entry("#{index}.jpg", &1))
end)
|> Zstream.zip()
end
end
When I request this from chrome, the download will start and it will show the download as resuming
:
When the download is around 200mb, the download will fail and I will get a Network Error
in chrome:
I will not get any error message in the backend, it will just stop sending chunks: