Summary#
File.Url(path) builds the URL a browser uses to fetch an app file — it prepends the app-file serving route to an
app-relative path. File.Url("public/products/latte.png") returns /_osy/files/public/products/latte.png.
It is pure (it touches no storage — it just formats a string), which is what lets it appear directly in a render
argument, unlike the File.* read/write effects. The canonical use is a photo on a page:
Image(src: File.Url(item.ImagePath), alt: item.Name);Like the rest of the File.* surface it is gated on using Osyrin.Storage; — an app that hasn't opted into
storage gets a compile error, not a silently-built URL.
Signature#
using Osyrin.Storage;
string File.Url(string path)Description#
File.Url is the display half of the file story. You write a file to the app-file store under a public/ path
(with a File.WriteAllBytes("public/…", bytes) effect, or via an upload), keep that path on a record
(CatalogItem.ImagePath = "public/products/latte.png"), and render it with Image(src: File.Url(item.ImagePath)).
Two things make it safe on a public, anonymous page:
- Only
public/files are servable anonymously. A browser<img>sends no auth, so the serving endpoint returns only files written underpublic/(every other path is a 404). Build your URLs frompublic/paths. - It's a pure value, evaluated where it's needed. In a
[Render(CSR)]page the URL is built on the client as the image renders; in a server-rendered page it's built on the server. Either way there's no round-trip and no data read — it's string formatting.
File.Url does not check that the file exists — it only formats the path. A missing file simply 404s when the
browser fetches it, exactly like any other broken image URL.
Examples#
A public product catalog — each item's stored public/ path becomes an <img src>:
using Osyrin.Storage;
entity CatalogItem {
string Name;
string ImagePath; // e.g. "public/products/latte.png"
security { allow read when IsAuthenticated || IsAnonymous; } // anonymous read — a public catalog
}
[Page("/catalog")]
[AllowAnonymous]
[Render(CSR)]
component CatalogPage() {
var items = CatalogItem.ToList(); // bound to a field, so the render has an answer to "when again?"
render {
Stack {
foreach (var item in items) {
Image(src: File.Url(item.ImagePath), alt: item.Name);
}
}
}
}Writing the file first (an admin action), then saving its path on the record:
using Osyrin.Storage;
string SavePhoto(string sku, byte[] photo) {
var path = "public/products/" + sku + ".png";
File.WriteAllBytes(path, photo);
return path; // store this on CatalogItem.ImagePath
}See also#
- [Searchable] — the other capability-gated surface (
using Osyrin.Memory;— searchable text) - style props — styling the image and its container