Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Elixir downloading files

notebooks/downloader.livemd

Elixir downloading files

Mix.install([
  {:bumblebee, "~> 0.5.0"},
  {:req, "~> 0.5.0"},
  {:kino, "~> 0.13.0"}
])

Image source URL

source_url = "https://elixir-lang.org/images/logo/logo.png"

Destination path

downloads_dir = Path.join(System.tmp_dir!(), "downloads")
File.mkdir_p!(downloads_dir)

extname = Path.extname(source_url)
destination_path = Path.join(downloads_dir, "elixir_logo" <> extname)

Req (binary)

%{status: 200} = response = Req.get!(source_url)
<<_::binary>> = response.body

Req (file)

Req.get!(source_url, into: File.stream!(destination_path))
File.read!(destination_path)

Bumblebee (file)

Bumblebee.Utils.HTTP.download(source_url, destination_path)
File.read!(destination_path)

Req + Finch + ProgressBar

defmodule MNishiguchi.Utils.HTTP do
  def download(source_url, req_options \\ []) do
    case Req.get(source_url, [finch_request: &amp;finch_request/4] ++ req_options) do
      {:ok, response} -> {:ok, response.body}
      {:error, exception} -> {:error, exception}
    end
  end

  def download!(source_url, req_options \\ []) do
    Req.get!(source_url, [finch_request: &amp;finch_request/4] ++ req_options).body
  end

  defp finch_request(req_request, finch_request, finch_name, finch_options) do
    acc = Req.Response.new()

    case Finch.stream(finch_request, finch_name, acc, &amp;handle_finch_stream/2, finch_options) do
      {:ok, response} -> {req_request, response}
      {:error, exception} -> {req_request, exception}
    end
  end

  defp handle_finch_stream({:status, status}, response), do: %{response | status: status}

  defp handle_finch_stream({:headers, headers}, response) do
    req_headers = headers |> Enum.map(fn {k, v} -> {k, List.wrap(v)} end) |> Map.new()
    total_size = req_headers |> Map.fetch!("content-length") |> List.first() |> String.to_integer

    response
    |> Map.put(:headers, req_headers)
    |> Map.put(:private, %{total_size: total_size, downloaded_size: 0})
  end

  defp handle_finch_stream({:data, data}, response) do
    new_downloaded_size = response.private.downloaded_size + byte_size(data)
    ProgressBar.render(new_downloaded_size, response.private.total_size, suffix: :bytes)

    response
    |> Map.update!(:body, &amp;(&amp;1 <> data))
    |> Map.update!(:private, &amp;%{&amp;1 | downloaded_size: new_downloaded_size})
  end
end
MNishiguchi.Utils.HTTP.download!(source_url, into: File.stream!(destination_path))
File.read!(destination_path)
<<_::binary>> = data = MNishiguchi.Utils.HTTP.download!(source_url)
data