Repository navigation
Releases: pehbehbeh/fil
Releases · pehbehbeh/fil
Release list
v0.2.0
Added
Fil.Plug.send_file/3sends 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, pluscontent_type:anddisposition:. A missing file
returns the error before anything is sent, so anaction_fallbackcan answer it.
(#39)Fil.disk/1takessigned_url:with default options forFil.signed_url/3on that disk (expires_in:,
disposition:andif_exists:), so a disk from config can give every signed URL a day and every download the
:attachmentdisposition. 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/3takestype: :regularortype: :directoryto list only files or only directories. With
recursive: true,type: :directorylists every directory of the subtree.
(#40)Fil.wildcard/2,3lists 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:), plustype:. No storage matches patterns itself, so
Fillists one directory level per segment, and the whole subtree once from a**on, the same on every adapter.
(#40)Fil.exists?/3andFil.stat/3take the sametype:option, andFil.regular?/1,2checks for a file. On S3,
Fil.regular?/1sends oneHEADandFil.dir?/1one 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.Validationrefuses writes by size (max_size:,min_size:), by content type (content_types:, checked
against the content's magic bytes, so a GIF named.pngor HTML in a.txtfails, andimage/*doesn't take SVG)
and by extension, plus acheck:of your own. A refused write returnsFil.InvalidContentErrorand 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 unlesspresigned_uploads: :check_declaredtrusts its signed content type.
(#36)Fil.Op.scan_content/4andFil.Op.scan_result/4let 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 newFil.InvalidContentErrorfrom a scan: the write returns the error and writes nothing, on
every adapter, also when the scan raises after the last chunk.Fil.Pluganswers an upload refused this way with a
413(too large), a415(content type or extension) or a422. (#33)Fil.read/3andFil.stream/3takeoffset:andlength:to read part of a file, on every adapter: S3 sends a
Rangeheader, the local disk reads from the offset, and the memory disk returns a sub-binary. Plugins that
transform reads get the whole file andFilcuts the part from their result, so offsets count bytes of the content
as you get it. (#35)Fil.Plugsendsetag,last-modifiedandaccept-ranges: bytes, answersif-none-matchandif-modified-since
with a304, and aGETwith one byte range with a206(416for 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/2lets the browser upload files straight to a disk, on every adapter: S3 with a presigned PUT,
local and memory disks throughFil.Plug. The consume functions then check each file instead of writing it, and
refuse one that's older than its upload URL or larger thanmax_file_size. The browser side is one import in
app.js,import {uploaders} from "phoenix-colocated/fil". (#26)Fil.signed_url/3takescontent_type:,size:andif_exists: :errorformethod: :put, and the upload has to
match them, on S3 and throughFil.Plug: anothercontent-typeorcontent-lengthis a403, and with
if_exists: :errorthe URL writes the file once and can't replace it.
(#26)Fil.LiveView.upload_field/1is 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/2handles
its cancel buttons, andFil.LiveView.upload_error/2turns LiveView's and Fil's upload errors into
{msgid, bindings}for the app'stranslate_error/1. The Phoenix guide
shows a complete form. (#27)Fil.Ecto.Refstores refs in Ecto schemas: the column holds the path, and loading returns aFil.Refon the disk
the field names.{:array, Fil.Ecto.Ref}holds several files in order, andFil.Ecto.Ref.removed/2returns the refs
a changeset drops, to delete after the commit. A ref fits a field whenFil.Disk.same_storage?/2finds 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.LiveViewstores LiveView uploads on a disk:consume_uploaded_entries/4streams each file intoFil.write/4at
a path you choose, and never replaces a file unless you passif_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/1returns the disk for a disk, a 0-arity function or an MFA.Fil.Plug'sdisk:goes through it
and raises anArgumentErrorwhen 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,1creates 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/1returns the path for tools that need one,Fil.Tmp.give_away/2hands the
directory to another process, andFil.Tmp.cleanup/1removes it earlier.
(#18)- A Fil disk smart cell for Livebook, which generates the
Fil.disk/1call 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/3is a file field for Livebook that writes each upload to a directory of a disk, streamed from the
file Livebook keeps.writable: trueonFil.Kino.browser/3adds it below the browser, together with a delete
button for each file. (#20)Fil.Kino.browser/3browses 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.Thumbnailswrites smaller copies of images when they're written, sized per variant, and deletes, copies
and renames them with the image. They go underthumbnails/, or wherever a:variant_pathfunction puts them, such
as next to the image.generate/1makes them later, withmode: :manualor 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 fromFil.stream/3is a[:fil, :stream, ...]span of its own, with the bytes read. See
Fil.Telemetry.
(#12) Fil.Telemetry.attach_default_logger/1logs 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_sizeoption. 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:crc32checksum covers the whole
file, however it's uploaded. A:sha256or:sha1checksum of an upload in parts covers each part, and S3 stores a
checksum of those: `Fil.sta...