diff --git a/lib/livebook/session.ex b/lib/livebook/session.ex
index 91781ac94ad..56f8337b5a7 100644
--- a/lib/livebook/session.ex
+++ b/lib/livebook/session.ex
@@ -684,6 +684,18 @@ defmodule Livebook.Session do
end
end
+ @doc """
+ Looks up file entry with the given name and returns a local path
+ for accessing the file.
+
+ When a file is available remotely, it is first downloaded into a
+ cached location.
+ """
+ @spec fetch_file_entry_path(pid(), String.t()) :: {:ok, String.t()} | {:error, String.t()}
+ def fetch_file_entry_path(pid, name) do
+ GenServer.call(pid, {:fetch_file_entry_path, name}, :infinity)
+ end
+
@doc """
Closes one or more sessions.
@@ -990,6 +1002,14 @@ defmodule Livebook.Session do
{:reply, :ok, state}
end
+ def handle_call({:fetch_file_entry_path, name}, from, state) do
+ file_entry_path(state, name, fn reply ->
+ GenServer.reply(from, reply)
+ end)
+
+ {:noreply, state}
+ end
+
@impl true
def handle_cast({:set_notebook_attributes, client_pid, attrs}, state) do
client_id = client_id(state, client_pid)
diff --git a/lib/livebook_web/controllers/session_controller.ex b/lib/livebook_web/controllers/session_controller.ex
index ce64fc18aca..6d8e6f0d66c 100644
--- a/lib/livebook_web/controllers/session_controller.ex
+++ b/lib/livebook_web/controllers/session_controller.ex
@@ -39,6 +39,16 @@ defmodule LivebookWeb.SessionController do
end
end
+ def download_file(conn, %{"id" => id, "name" => name}) do
+ with {:ok, session} <- Sessions.fetch_session(id),
+ {:ok, path} <- Session.fetch_file_entry_path(session.pid, name) do
+ send_download(conn, {:file, path}, filename: name)
+ else
+ _ ->
+ send_resp(conn, 404, "Not found")
+ end
+ end
+
def download_source(conn, %{"id" => id, "format" => format}) do
case Sessions.fetch_session(id) do
{:ok, session} ->
diff --git a/lib/livebook_web/live/home_live/session_list_component.ex b/lib/livebook_web/live/home_live/session_list_component.ex
index 2a1d8be1580..a186ac1b58f 100644
--- a/lib/livebook_web/live/home_live/session_list_component.ex
+++ b/lib/livebook_web/live/home_live/session_list_component.ex
@@ -151,7 +151,7 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
<.menu_item>
<.remix_icon icon="download-2-line" />
diff --git a/lib/livebook_web/live/session_live/export_live_markdown_component.ex b/lib/livebook_web/live/session_live/export_live_markdown_component.ex
index 9aaebcca309..b1b80a24a39 100644
--- a/lib/livebook_web/live/session_live/export_live_markdown_component.ex
+++ b/lib/livebook_web/live/session_live/export_live_markdown_component.ex
@@ -51,7 +51,7 @@ defmodule LivebookWeb.SessionLive.ExportLiveMarkdownComponent do
class="icon-button"
aria-label="download source"
href={
- ~p"/sessions/#{@session.id}/export/download/livemd?include_outputs=#{@include_outputs}"
+ ~p"/sessions/#{@session.id}/download/export/livemd?include_outputs=#{@include_outputs}"
}
>
<.remix_icon icon="download-2-line" class="text-lg" />
diff --git a/lib/livebook_web/live/session_live/files_list_component.ex b/lib/livebook_web/live/session_live/files_list_component.ex
index ca9deca6a03..01798ada92d 100644
--- a/lib/livebook_web/live/session_live/files_list_component.ex
+++ b/lib/livebook_web/live/session_live/files_list_component.ex
@@ -97,6 +97,12 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do
Clear cache
+ <.menu_item>
+
+ <.remix_icon icon="download-2-line" />
+ Download
+
+
<.menu_item variant={:danger}>