Fil.Kino (Fil v0.2.0)

Copy Markdown View Source

Browses disks in Livebook, uploads files to them, and adds a smart cell that builds a disk.

Mix.install([{:fil, "~> 0.2"}, {:kino, "~> 0.19"}])

disk = Fil.disk(adapter: Fil.Adapter.Local, root: "/tmp/fil")
Fil.Kino.browser(disk)

Needs Kino, an optional dependency of Fil.

Browser

browser/3 shows one directory of a disk at a time: breadcrumbs back to the root, and a table with the name, size, modification time and type of each entry. Clicking a directory opens it. Clicking a file shows a preview below the table, for images (PNG, JPEG, GIF and WebP) and text, and each file has a download button.

The browser only reads by default, so a stray click in a notebook can't change a production disk. With writable: true, each file also gets a delete button, and an upload field below the table writes into the current directory.

It lists the directory when it's shown and when you open another one, so files that other cells write show up after a click on Refresh.

It works the same on every disk. A memory disk uses the store of the process that built the browser, which in Livebook is the evaluator of the cell. Livebook evaluates all regular sections in one process, so after Fil.Adapter.Memory.checkout/0 in one cell, a browser in any regular section sees the files. A branching section has a process of its own, and needs a checkout/0 of its own.

Upload

upload/3 is a file field that writes each file you pick straight to a directory of the disk:

Fil.Kino.upload(disk, "reports", accept: [".pdf"])

Livebook keeps an upload as a file in the notebook's runtime, and the field streams it from there into Fil.write/4, so large files don't have to fit in memory. The line below the field says what was written, or why not. An upload never replaces a file unless you pass if_exists: :overwrite.

Smart cell

With Fil and Kino installed, Livebook offers a Fil disk smart cell. It's a form for a Local, S3 or Memory disk that generates the Fil.disk/1 call, such as:

disk =
  Fil.disk(
    adapter: Fil.Adapter.S3,
    bucket: "reports",
    region: "eu-central-1",
    access_key_id: System.fetch_env!("LB_AWS_ACCESS_KEY_ID"),
    secret_access_key: System.fetch_env!("LB_AWS_SECRET_ACCESS_KEY")
  )

The S3 credentials are Livebook secrets you pick in the form, so they're never part of the notebook. Without them, the disk accesses a public bucket without signing. The session token is optional, for temporary credentials such as those from AWS STS or SSO. Empty fields are left out, so the adapter's defaults apply.

The path-style switch shows what the disk does. Fil.Adapter.S3 uses path-style URLs when there's an endpoint, so the switch turns on when you enter one and off when you clear it, until you switch it the other way. The code sets path_style: only when the switch differs from that default.

For a memory disk, the cell calls Fil.Adapter.Memory.checkout/0 first. The store belongs to the evaluator (see Browser for what that means for sections), and it's gone when the runtime restarts.

Summary

Functions

Returns a file browser for a disk or a ref, starting at the root or at path.

Returns a file browser. See browser/1.

Returns a file browser. See browser/1.

Returns a file field that writes each uploaded file to the root of a disk, or to path.

Returns a file field that writes to a disk. See upload/1.

Returns a file field that writes to a disk. See upload/1.

Functions

browser(disk)

@spec browser(Fil.Disk.t() | Fil.Ref.t()) :: Kino.JS.Live.t()

Returns a file browser for a disk or a ref, starting at the root or at path.

Fil.Kino.browser(disk)
Fil.Kino.browser(disk, "reports")
Fil.Kino.browser(disk, "reports", writable: true)
Fil.Kino.browser(reports)
Fil.Kino.browser(reports, max_download_size: 1_000_000_000)

It's a Kino.JS.Live, and with writable: true a Kino.Layout of the browser and the upload field. Both render as the result of a cell, with Kino.render/1 and inside a Kino.Layout or a Kino.Frame. Building the browser does no I/O: the directory is listed when the browser is shown, and a storage error shows up in the browser instead of raising.

Options

  • :max_preview_size (pos_integer/0) - Files larger than this, in bytes, get no preview, only the download button. 1 MiB by default. The size comes from the listing, so nothing is read to decide, and a file that grew since isn't caught. The default value is 1048576.

  • :max_download_size (pos_integer/0) - Files larger than this, in bytes, can't be downloaded from the browser. 100 MiB by default. A download reads the whole file into memory and sends it to the browser through Livebook, so large files are better copied with Fil.cp/3 or read with Fil.stream/3. The size is checked against the listing, so a file that grew since isn't caught. The default value is 104857600.

  • :writable (boolean/0) - Adds a delete button to each file, and an upload field for the current directory below the browser (see upload/3). A delete asks for confirmation first, and only deletes files, never directories. The upload never replaces a file. The default value is false.

browser(disk, path)

@spec browser(Fil.Disk.t(), Path.t()) :: Kino.JS.Live.t()
@spec browser(Fil.Ref.t(), keyword()) :: Kino.JS.Live.t() | Kino.Layout.t()

Returns a file browser. See browser/1.

browser(disk, path, opts)

@spec browser(Fil.Disk.t(), Path.t(), keyword()) :: Kino.JS.Live.t() | Kino.Layout.t()

Returns a file browser. See browser/1.

upload(disk)

@spec upload(Fil.Disk.t() | Fil.Ref.t()) :: Kino.Layout.t()

Returns a file field that writes each uploaded file to the root of a disk, or to path.

Fil.Kino.upload(disk)
Fil.Kino.upload(disk, "reports", accept: [".pdf"], on_upload: &IO.inspect/1)
Fil.Kino.upload(reports)
Fil.Kino.upload(reports, if_exists: :overwrite)

A file is written under the name it had on your computer. A name that isn't a plain file name, such as .. or one with a / or a \, is refused. It returns a Kino.Layout of Kino's file input and a status line, which renders as the result of a cell. The field stays usable after an error.

Options

  • :label (String.t/0) - The label of the field. "Upload to memory:reports" for a memory disk and the path "reports", by default.

  • :accept - The file types the field accepts, as a list of extensions (".pdf") or MIME types ("image/*"), or :any. Passed to Kino.Input.file/2, which raises on an empty list. The default value is :any.

  • :if_exists - What to do when the directory already has a file of that name. :error keeps the file and shows a Fil.AlreadyExistsError, :overwrite replaces it. See Fil.write/4. The default value is :error.

  • :on_upload (function of arity 1) - Called with the Fil.Ref of each file after it was written, for example to process it. It runs in a process Kino starts for the field.

upload(disk, path)

@spec upload(Fil.Disk.t(), Path.t()) :: Kino.Layout.t()
@spec upload(Fil.Ref.t(), keyword()) :: Kino.Layout.t()

Returns a file field that writes to a disk. See upload/1.

upload(disk, path, opts)

@spec upload(Fil.Disk.t(), Path.t(), keyword()) :: Kino.Layout.t()

Returns a file field that writes to a disk. See upload/1.