Osy#betaRun it on Osyrin: osyrin.com

Reference / Storage

Image.Thumbnail, Resize and Convert (a stored image, transformed)

using Osysharp.Images; · FileAsset Image.Thumbnail(FileAsset source, int max) · Image.Resize(source, w, h) · Image.Convert(source, mime)

Three server-side transforms over a stored image, each answering a NEW `FileAsset`: `Thumbnail` fits the image within `max` pixels on its longest side (aspect kept, JPEG), `Resize` makes it exactly `w`×`h` in its own format, `Convert` re-encodes the same pixels to another format. Nothing is generated on upload; you make a variant when you ask for one, and it is stored and quota-counted like any other file. Show one with `Image(fileAsset: variant.Id)`.

preview1 example compiled by CIstoragefilesimagesstdlib

Summary#

Image.* is a standard-library surface, like Http.*, that a capability gates: using Osysharp.Images; is what makes it resolvable, and the capability declares no table of its own because every op consumes a FileAsset and produces one. The result is a new file — grantable, counted against the app's storage quota, and deduplicated by content, so asking twice for the same thumbnail stores it once.

A FileAsset is a ROW and carries no path, so File.Url and File.SignedUrl — whose argument is an app-relative path — cannot address one. To put a variant on a page, give the element the row: Image(fileAsset: thumb.Id). See Showing a picture on a page.

Nothing runs at upload time. A photograph, a chosen file (see upload) or a capture (see camera and microphone) is stored as it arrived; the variant exists the moment your code asks for it, in the same unit of work.

Signature#

using Osysharp.Images;      // gates the surface; the results are Osysharp.Storage.FileAsset, so that capability is in play too

FileAsset small = Image.Thumbnail(source, 128);          // fit within 128 px on the longest side, aspect kept, JPEG
FileAsset exact = Image.Resize(source, 800, 600);        // exactly 800×600, in the source's own format
FileAsset png   = Image.Convert(source, "image/png");    // the same pixels, re-encoded

Description#

The three ops#

OpAnswersFormat
Image.Thumbnail(source, max)the image scaled to fit within max px on its longest side, aspect preservedJPEG
Image.Resize(source, w, h)the image at exactly w×hthe source's
Image.Convert(source, mime)the same pixelsthe mime you name

Convert writes image/jpeg, image/png, image/webp, image/gif or image/bmp; any other target throws, naming those five. It reads those formats too (an animated GIF as its first frame).

Every op answers an upright picture with no metadata: a phone photo whose EXIF says "rotate me" has the rotation applied to its pixels, and the new file carries no EXIF at all — no camera, no timestamp, no GPS position. A JPEG has no transparency, so a transparent pixel is laid on white.

Each is a call that leaves the function's own process — a server-side transform — so inside a workflow it is its own durable step, the same as any other outside effect.

What can go wrong#

The source must be a stored image with bytes: a missing asset, an empty one, or a file that is not a decodable image throws. The new bytes count against the app's storage quota; when they would exceed it the write is refused with the storage budget's own exception, exactly as an upload would be.

Size is bounded both ways. A source larger than 64 megapixels (an 8000 × 8000 image, or a 48 MP phone photo, fits) throws before it is decoded — the size is read from the file's header, so a small file that declares an enormous image costs nothing. Resize throws for a target over 64 megapixels or over 16 384 px a side, and every size must be at least 1. All of these are ordinary exceptions to catch where the page has something sensible to show instead.

The thumbnail on a list#

The shape every app with an image on a row ends up writing — one function, called when the row is drawn:

using Osysharp.Storage;
using Osysharp.Images;

entity Receipt {
  [Required, MaxLength(140)] string Merchant;
  FileAsset? Photo;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

FileAsset Thumbnail(FileAsset source) {
  return Image.Thumbnail(source, 128);
}

demo/file-manager is the same one-liner in a whole app (model/functions.osy). What it puts on the page is Image(fileAsset: f.Id, alt: f.Name, w: 30) — a row is a row, whether it is the original or a variant, and the asset's own security decides who is served the bytes: a file that belongs to one person is refused to everyone else without the page doing anything about it.

Examples#

Two variants of one upload, both stored, both addressed by their own id:

FileAsset thumb = Image.Thumbnail(item.Photo, 128);   // in a function — an Image.* op is an effect
FileAsset web   = Image.Resize(item.Photo, 1200, 800);

…and each is shown by its own row, never by a URL the app builds:

Image(fileAsset: item.Thumb.Id, alt: item.Merchant, w: 128)

See also#

Related

Files (addressing something the app stores)

The platform stores a file two ways, and which one you hold decides how you address it. A PATH-keyed file lives at an…

Showing a picture on a page

`Image` is the element that shows a picture, and it addresses one of two ways. `src` takes a URL — for a file in the…

File.Url

Turns an app-relative file path into the public URL a browser can fetch it from — `File.Url("public/x.png")` returns…

File.SignedUrl

Mints a temporary, signed URL that lets an authorized browser fetch a PRIVATE app file — one stored outside `public/` —…

upload

`Upload(onUploaded: Ingest) { … }` is a file picker wearing whatever you put inside it. When someone chooses a file its…

camera and microphone

Take a photograph, record a video, or record audio. `Camera.Start()` asks for the device and `Camera()` shows the…