Summary#
A stored file is addressed by URL, and which URL you ask for is an access decision.
using Osyrin.Storage;
[Principal] entity User { [MaxLength(200)] string Email; }
entity Item { [Required, MaxLength(200)] string ImagePath; }
// Anyone may fetch this — it is an asset, served to whoever asks.
string PublicImage(Item item) { return File.Url(item.ImagePath); }
// This one belongs to somebody, so the URL carries its own expiring grant.
string PrivateReport(User user) { return File.SignedUrl("reports/" + user.Id + "/q3.pdf"); }Description#
File.Url is pure sugar over the serving route — an app-relative path becomes /_osy/files/…, which
means it works directly inside a render argument. It grants nothing: what it addresses is served to whoever asks,
so it is right for assets and wrong for anything a person owns.
File.SignedUrl is the other case, and it is the one to reach for by default when the file belongs to somebody: the URL carries its own time-limited grant, so sharing it is a decision with an expiry rather than a permanent one.
See also#
- File.Url — the public address, and where it is safe
- File.SignedUrl — a time-limited grant to one caller