Skip to main content

Image Processing

Every module that uses kernel.files() automatically gets a full image processing pipeline. Modules never need to implement their own resize, crop, or format conversion — the kernel handles it transparently.


Technical Stack

ComponentLibraryRole
Backendbimg (libvips, Go binding)Crop, resize, convert, compress. C library used by Netflix and AWS
Frontendreact-easy-cropInteractive crop editor: grid, zoom, drag, aspect ratio lock, rotation
Output formatsWebP, AVIF, PNG, JPEG, SVGAuto-selected by backend

Upload + Processing Pipeline

When a user uploads an image through the <ImageUpload> component or uploadImage(), the following steps execute automatically:

1. User selects file in browser

2. Frontend — react-easy-crop editor:
Grid crop overlay (Instagram-style)
Zoom: pinch gesture or slider
Drag to reposition crop area
Aspect ratio lock: 1:1, 16:9, 3:1 (configured per module)
Rotation

3. Frontend sends:
Original file + crop parameters { x, y, width, height, zoom }

4. Backend — bimg/libvips:
Crop by coordinates
Resize to target dimensions
Convert: WebP by default, PNG if transparency detected
Compress: quality 80–90% (visually lossless, size −60–80%)
Generate thumbnail presets (icon_32 + card_300 always, others on-demand)
Timeout: 30 s (FILES_IMAGE_PROCESSING_TIMEOUT_SEC)
→ on timeout: original saved, thumbnails = pending (background retry)
Limit: 10 MB max (FILES_MAX_IMAGE_SIZE_MB)
→ above limit: 413 Payload Too Large

5. Save to S3:
Original file
Processed file (cropped + resized + converted)
Auto-generated thumbnails

SDK — uploadImage()

Upload an image with crop parameters in a single call:

import { kernel } from '@platform/sdk-core';

const result = await kernel.files().uploadImage({
file: imageFile, // File | Blob | Buffer
filename: 'profile-photo.png',
bucket: 'avatars',
crop: {
x: 120,
y: 80,
width: 400,
height: 400,
zoom: 1.2,
},
outputFormat: 'webp', // 'webp' | 'avif' | 'png' | 'jpeg' — default 'webp'
quality: 85, // 1–100 — default 85
aspectRatio: '1:1',
});

// result:
// {
// fileId: '01j9paf1l000000000000000',
// originalUrl: 'https://api.septemcore.com/v1/files/01j9paf1l...',
// processedUrl: 'https://api.septemcore.com/v1/files/01j9paf1l.../processed',
// thumbnails: {
// icon_32: 'https://api.septemcore.com/v1/files/01j9paf1l.../thumbnail/icon_32',
// card_300: 'https://api.septemcore.com/v1/files/01j9paf1l.../thumbnail/card_300'
// },
// size: 84320,
// compressionRatio: '72%',
// status: 'available'
// }

SDK — processImage()

Process an already-uploaded image (run or re-run bimg operations):

const result = await kernel.files().processImage({
fileId: '01j9paf1l000000000000000',
operations: {
crop: { x: 0, y: 0, width: 800, height: 600 },
resize: { width: 400, height: 300 },
format: 'avif',
quality: 80,
rotate: 90,
},
});

// result:
// {
// fileId: '01j9paf1l000000000000000',
// processedUrl: 'https://api.septemcore.com/v1/files/01j9paf1l.../processed',
// size: 61440,
// status: 'available'
// }

REST — POST /files/:id/process

POST https://api.septemcore.com/v1/files/01j9paf1l000000000000000/process
Authorization: Bearer <access_token>
Content-Type: application/json

{
"operations": {
"crop": { "x": 0, "y": 0, "width": 800, "height": 600 },
"resize": { "width": 400, "height": 300 },
"format": "avif",
"quality": 80,
"rotate": 90
}
}

Response 200 OK:

{
"fileId": "01j9paf1l000000000000000",
"processedUrl": "https://api.septemcore.com/v1/files/01j9paf1l.../processed",
"size": 61440,
"status": "available"
}

Output Format Selection

FormatWhen usedNotes
webpDefault for all images30–40% smaller than JPEG at same quality
avifExplicit request or AVIF-capable CDN config50% smaller than JPEG, slower encode
pngTransparency detected or explicitly requestedLossless, larger file size
jpegExplicit request for JPEG outputLegacy compatibility
svgPassthrough only — not processed by bimgSVG is not rasterized

Processing Limits and Timeouts

ParameterValueEnv variable
Max image size10 MBFILES_MAX_IMAGE_SIZE_MB=10
Processing timeout30 secondsFILES_IMAGE_PROCESSING_TIMEOUT_SEC=30
Timeout behaviourOriginal saved; thumbnails = pending → background retry
Quality range1–100 (default 85)
Max output dimensions8 000 × 8 000 pxlibvips internal limit

Frontend — ImageUpload Component

UI Shell provides a ready-made <ImageUpload> component with the built-in crop editor. Modules drop it in without implementing any crop UI:

import { ImageUpload } from '@platform/ui-shell';

<ImageUpload
bucket="avatars"
aspectRatio="1:1"
maxSizeMb={10}
outputFormat="webp"
presets={['icon_32', 'avatar_64', 'card_300']}
onUploadComplete={(result) => {
// result.fileId, result.thumbnails.avatar_64, result.processedUrl
setAvatarUrl(result.thumbnails.avatar_64);
}}
/>
PropDescription
bucketTarget logical bucket (avatars, assets, documents, exports, modules)
aspectRatioLock crop ratio: '1:1' for avatars, '3:1' for banners, '16:9' for covers
maxSizeMbClient-side size validation before upload (in addition to server 10 MB limit)
outputFormatTarget format: 'webp' (default), 'avif', 'png', 'jpeg'
presetsThumbnail presets to generate. icon_32 and card_300 are always generated
onUploadCompleteCallback receiving the full upload result

Error Reference

ScenarioHTTPCode
File exceeds 10 MB413PAYLOAD_TOO_LARGE
Processing timeout (30 s)202Original saved; thumbnails retried in background
Invalid crop coordinates400validation-error
Unsupported source format422UNSUPPORTED_FORMAT
File not found404not-found