Skip to content

Releases: pehbehbeh/fil

Release list

v0.2.0

Choose a tag to compare

@pehbehbeh pehbehbeh released this 06 Oct 23:19

Added

  • Fil.Plug.send_file/3 sends a file from a disk as the response of a controller, on every adapter, with the same
    etag, 304, ranges and streaming as the mounted plug, plus content_type: and disposition:. A missing file
    returns the error before anything is sent, so an action_fallback can answer it.
    (#39)
  • Fil.disk/1 takes signed_url: with default options for Fil.signed_url/3 on that disk (expires_in:,
    disposition: and if_exists:), so a disk from config can give every signed URL a day and every download the
    :attachment disposition. The options of a call replace them, and a default that doesn't fit the method (a
    disposition on an upload) is left out. (#37)
  • Fil.ls/3 takes type: :regular or type: :directory to list only files or only directories. With
    recursive: true, type: :directory lists every directory of the subtree.
    (#40)
  • Fil.wildcard/2,3 lists the paths of a disk that match a pattern, such as "media/*/{withText,withoutText}", with
    Path.wildcard/2's syntax and dotfile rule (match_dot:), plus type:. No storage matches patterns itself, so
    Fil lists one directory level per segment, and the whole subtree once from a ** on, the same on every adapter.
    (#40)
  • Fil.exists?/3 and Fil.stat/3 take the same type: option, and Fil.regular?/1,2 checks for a file. On S3,
    Fil.regular?/1 sends one HEAD and Fil.dir?/1 one listing, where a missing path took both before. The listing
    that checks for a directory now asks for two keys instead of reading the whole directory.
    (#40)
  • Fil.Plugin.Validation refuses writes by size (max_size:, min_size:), by content type (content_types:, checked
    against the content's magic bytes, so a GIF named .png or HTML in a .txt fails, and image/* doesn't take SVG)
    and by extension, plus a check: of your own. A refused write returns Fil.InvalidContentError and writes nothing,
    and a stream fails as soon as it breaks a rule. Upload URLs are checked when they're signed, and a presigned S3 upload
    that would skip a content check is refused unless presigned_uploads: :check_declared trusts its signed content type.
    (#36)
  • Fil.Op.scan_content/4 and Fil.Op.scan_result/4 let a plugin read the content of a write or a read as it passes,
    without changing it. A stream keeps its chunks and its :size, so S3 still sends it in one request. A plugin refuses
    content by raising the new Fil.InvalidContentError from a scan: the write returns the error and writes nothing, on
    every adapter, also when the scan raises after the last chunk. Fil.Plug answers an upload refused this way with a
    413 (too large), a 415 (content type or extension) or a 422. (#33)
  • Fil.read/3 and Fil.stream/3 take offset: and length: to read part of a file, on every adapter: S3 sends a
    Range header, the local disk reads from the offset, and the memory disk returns a sub-binary. Plugins that
    transform reads get the whole file and Fil cuts the part from their result, so offsets count bytes of the content
    as you get it. (#35)
  • Fil.Plug sends etag, last-modified and accept-ranges: bytes, answers if-none-match and if-modified-since
    with a 304, and a GET with one byte range with a 206 (416 for a range outside the file, if-range
    honoured). Several ranges get the whole file. Signed URLs work with ranges too, so browsers can seek in videos
    served from a local disk. (#35)
  • A tour of the API as a Livebook notebook, under Guides in the docs and linked from the README
    with a "Run in Livebook" badge. (#34)
  • Fil.LiveView.external/2 lets the browser upload files straight to a disk, on every adapter: S3 with a presigned PUT,
    local and memory disks through Fil.Plug. The consume functions then check each file instead of writing it, and
    refuse one that's older than its upload URL or larger than max_file_size. The browser side is one import in
    app.js, import {uploaders} from "phoenix-colocated/fil". (#26)
  • Fil.signed_url/3 takes content_type:, size: and if_exists: :error for method: :put, and the upload has to
    match them, on S3 and through Fil.Plug: another content-type or content-length is a 403, and with
    if_exists: :error the URL writes the file once and can't replace it.
    (#26)
  • Fil.LiveView.upload_field/1 is an upload field for LiveView forms, with drag and drop, previews, progress, cancel
    buttons, and remove buttons for the files the record already has. It's styled with daisyUI like the components
    Phoenix 1.8 generates, and a class attribute per part replaces the defaults. Fil.LiveView.cancel_upload/2 handles
    its cancel buttons, and Fil.LiveView.upload_error/2 turns LiveView's and Fil's upload errors into
    {msgid, bindings} for the app's translate_error/1. The Phoenix guide
    shows a complete form. (#27)
  • Fil.Ecto.Ref stores refs in Ecto schemas: the column holds the path, and loading returns a Fil.Ref on the disk
    the field names. {:array, Fil.Ecto.Ref} holds several files in order, and Fil.Ecto.Ref.removed/2 returns the refs
    a changeset drops, to delete after the commit. A ref fits a field when Fil.Disk.same_storage?/2 finds its disk on
    the same storage: the same adapter and the same address, which adapters return from the new optional callback
    c:Fil.Adapter.address/1 (the root on a local disk, the endpoint, region, bucket and prefix on S3), so plugins and
    rotated credentials don't count. Ecto is a new optional dependency. (#25)
  • Fil.LiveView stores LiveView uploads on a disk: consume_uploaded_entries/4 streams each file into Fil.write/4 at
    a path you choose, and never replaces a file unless you pass if_exists: :overwrite. If a write fails, it deletes the
    files it wrote and keeps the entries, so the form can be submitted again. It needs Phoenix LiveView 1.2, an optional
    dependency. The Phoenix guide walks through a form.
    (#23)
  • Fil.Disk.resolve/1 returns the disk for a disk, a 0-arity function or an MFA. Fil.Plug's disk: goes through it
    and raises an ArgumentError when its function returns something other than a disk. It takes a function only as a
    capture such as &MyApp.Storage.uploads/0, because plug options can't hold an anonymous function.
    (#22)
  • Fil.tmp/0,1 creates a temporary directory and returns a ref to it, or to a file in it, on a local disk, so the
    whole API works on it. The directory belongs to the calling process and is removed when that process exits, also
    when it's killed. Fil.Tmp.path/1 returns the path for tools that need one, Fil.Tmp.give_away/2 hands the
    directory to another process, and Fil.Tmp.cleanup/1 removes it earlier.
    (#18)
  • A Fil disk smart cell for Livebook, which generates the Fil.disk/1 call for a Local, S3 or Memory disk, with the S3
    credentials from Livebook secrets. It's there when Kino is installed.
    (#21)
  • Fil.Kino.upload/3 is a file field for Livebook that writes each upload to a directory of a disk, streamed from the
    file Livebook keeps. writable: true on Fil.Kino.browser/3 adds it below the browser, together with a delete
    button for each file. (#20)
  • Fil.Kino.browser/3 browses a disk in Livebook: one directory at a time, with a preview of images and text and a
    download button. Kino is a new optional dependency. (#19)
  • Fil.Plugin.Thumbnails writes smaller copies of images when they're written, sized per variant, and deletes, copies
    and renames them with the image. They go under thumbnails/, or wherever a :variant_path function puts them, such
    as next to the image. generate/1 makes them later, with mode: :manual or for images stored before. It needs the
    optional Vix dependency.
    (#14)
  • Telemetry events for every operation on every disk, [:fil, :op, :start], [:fil, :op, :stop] and
    [:fil, :op, :exception], with the operation, the disk, the path, the error and the bytes read or written. Each read
    of a stream from Fil.stream/3 is a [:fil, :stream, ...] span of its own, with the bytes read. See
    Fil.Telemetry.
    (#12)
  • Fil.Telemetry.attach_default_logger/1 logs every operation and every read of a stream, with its duration.
    (#12)
  • S3 uploads a stream without a size, a stream with checksum:, and content over 5 GiB in parts (a multipart upload),
    with one part in memory at a time: 8 MiB by default, set with the new :part_size option. A stream that fits in one
    part is still one PutObject. A part that fails because the storage is unavailable is sent once more, a second later.
    A failed upload is aborted, and so is the upload of a process that's killed. A :crc32 checksum covers the whole
    file, however it's uploaded. A :sha256 or :sha1 checksum of an upload in parts covers each part, and S3 stores a
    checksum of those: `Fil.sta...
Read more

v0.1.0

Choose a tag to compare

@pehbehbeh pehbehbeh released this 27 Sep 00:42
  • First release.