Skip to main content
Wildo.ai Coming soon

Files and storage

Show a lighter preview of the same image

Eligible images can be resized when served, keeping one stored original and the same authorized file access path.

A full-size image is represented by a smaller preview of the same scene.

Show a lighter preview of the same image

A small avatar or attachment preview should not need a full-resolution image. Wildo can derive a smaller rendition when the file is requested, using the same authorized route as the original.

The original remains in storage. Supported images are resized without enlargement and returned as WebP, while the field can explicitly disable derived renditions.

Example — A photo stays full size when downloaded

An attachment card requests a thumbnail of a large photo. Opening the original still retrieves the uploaded image. The application does not need to keep a second file record or arrange deletion of a separate preview object.

For engineers

Request a rendition, not a different file

Use ?variant=thumbnail on the file serve URL. The standard preview surfaces use the file URL helpers; an original request stays on the original byte path. The service first respects imageConstraints.generateThumbnails: false, which refuses a thumbnail request rather than returning a larger original against the author’s choice.

Otherwise, recorded MIME type and size determine eligibility. The current transformation handles supported raster images up to a 25 MB source cap. SVG is excluded.

Understand the image transformation

generateImageThumbnail in image-thumbnail.backend.utils.ts loads Sharp lazily, applies orientation and fits the image inside a 512-pixel square without enlargement. The whole function is shown because its fallback is part of the contract.

export async function generateImageThumbnail(source: Buffer): Promise<GeneratedThumbnail | null> {
  const sharp = await loadSharpFactory();
  if (!sharp) return null; // sharp not available → caller serves the original bytes
  try {
    const data = await sharp(source, { failOn: 'none' })
      .rotate() // bake EXIF orientation into pixels; sharp then strips metadata by default
      .resize(THUMBNAIL_MAX_EDGE_PX, THUMBNAIL_MAX_EDGE_PX, {
        fit: 'inside',
        withoutEnlargement: true,
      })
      .webp({ quality: 80 })
      .toBuffer();
    return { data, contentType: THUMBNAIL_CONTENT_TYPE };
  } catch {
    return null;
  }
}

Keep fallback and caching expectations precise

Successful generation strips embedded metadata, including EXIF location data, from the derived WebP. If resizing is unavailable or fails, the service can return the original bytes with their original metadata. Non-images and oversized sources also use the original path. Storage failures and authorization refusals are not converted into a successful fallback.

Thumbnail responses use a private one-day cache policy and a versioned validator. Preview URL helpers carry the file version so a changed file can get a new cache identity. This reduces repeated transfers to a client; it does not create a shared derivative cache or guarantee that a browser’s already-cached image can be recalled immediately when access changes.

Declare the preview policy and use the URL helper

This illustrative field permits JPEG and PNG uploads and allows the standard derived preview:

import { z_file } from '@wildo-ai/zod-decorators';

const photo = z_file({
  multiple: false,
  allowedMimeTypes: ['image/jpeg', 'image/png'],
  imageConstraints: {
    generateThumbnails: true,
  },
});

After obtaining the authenticated serve URL for that record and file, select the rendition with the public helper. This example assumes serveBase is the resolved resource-derived base and fileId comes from the stored file reference:

import { buildFileServeThumbnailUrl } from '@wildo-ai/saas-frontend-lib';

const originalUrl = `${serveBase}/${encodeURIComponent(fileId)}`;
const previewUrl = buildFileServeThumbnailUrl(originalUrl);
// Adds ?variant=thumbnail, or &variant=thumbnail when a query already exists.

Fetch the preview through the same authenticated client as the original. The helper selects a rendition; it does not add authorization credentials or turn a protected resource route into a public image URL.

Field choiceResult of requesting a thumbnail
generateThumbnails: true, or omittedPermits derivation; eligible raster images use the fixed 512-pixel maximum edge
generateThumbnails: falseRefuses the thumbnail request; an explicit original request remains available under normal access checks
generateThumbnails: { sizes: [...] }Permits derivation, but the listed sizes do not control the current renderer
Permitted, but unsupported source or unavailable transformationUses the original byte path; do not rely on the preview request to strip metadata

Keep the original URL for full-resolution viewing. This is serve-time derivation of the same stored file, not a second attachment or an author-defined set of pre-generated image sizes.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.