# File.Url

> Turns an app-relative file path into the public URL a browser can fetch it from — `File.Url("public/x.png")` returns `/_osy/files/public/x.png`. Pure sugar over the serving route, so it works directly inside a render argument like `Image(src: File.Url(item.ImagePath))`.

<!-- id: storage-file-url · area: storage · stability: preview · html: https://osysharp.com/reference/storage/file-url/ -->

## Summary        {#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:

```osy syntax
Image(src: File.Url(item.ImagePath), alt: item.Name);
```

Like the rest of the `File.*` surface it is gated on **`using Osysharp.Storage;`** — an app that hasn't opted into
storage gets a compile error, not a silently-built URL.

## Signature      {#signature}
```osy syntax
using Osysharp.Storage;

string File.Url(string path)
```

## Description    {#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 under `public/` (every other path is a 404). Build your URLs from `public/` 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       {#examples}

A public product catalog — each item's stored `public/` path becomes an `<img src>`:

```osy title="a public catalog served straight from the public folder" test app=storage-file-url
using Osysharp.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:

```osy title="write the file, then store its path" test app=storage-file-url
using Osysharp.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       {#see-also}
- [[Searchable]](https://osysharp.com/reference/memory/searchable/) — the other capability-gated surface (`using Osysharp.Memory;` — searchable text)
- [style props](https://osysharp.com/reference/ui/styling/) — styling the image and its container
