|
| 1 | +# Image Upload Pipeline |
| 2 | + |
| 3 | +## Size Limits |
| 4 | + |
| 5 | +``` |
| 6 | +Browser (any size) → Compress → Server (10MB) → S3 (no limit) |
| 7 | +``` |
| 8 | + |
| 9 | +| Stage | Limit | Enforcement | |
| 10 | +|-------|-------|-------------| |
| 11 | +| Browser input | None | User can upload any size | |
| 12 | +| Browser → Server | 10MB | Client compresses until it fits | |
| 13 | +| Server validation | 10MB (base64 ~13.7MB) | DoS protection | |
| 14 | +| Server → S3 | None | MinIO accepts any size | |
| 15 | + |
| 16 | +## Browser Compression |
| 17 | + |
| 18 | +Located in `$lib/shared/logic/image-processing.ts`: |
| 19 | + |
| 20 | +```ts |
| 21 | +const SERVER_LIMIT = 10 * 1024 * 1024; // 10MB |
| 22 | +const QUALITY_STEPS = [0.9, 0.7, 0.5, 0.3]; |
| 23 | +const DIMENSION_STEPS = [1920, 1440, 1080, 720]; |
| 24 | +``` |
| 25 | + |
| 26 | +Algorithm: |
| 27 | +1. Start at 1920px, quality 0.9 |
| 28 | +2. If > 10MB, reduce quality (0.9 → 0.7 → 0.5 → 0.3) |
| 29 | +3. If all qualities fail, reduce dimension (1920 → 1440) |
| 30 | +4. Reset quality, repeat |
| 31 | +5. Max 16 attempts (4 qualities × 4 dimensions) |
| 32 | +6. Best-effort: if still > 10MB after all attempts, return smallest achieved |
| 33 | + |
| 34 | +## Server Validation |
| 35 | + |
| 36 | +Located in `$lib/data/private/storage.remote.ts`: |
| 37 | + |
| 38 | +```ts |
| 39 | +const MAX_BASE64_SIZE = Math.ceil(10 * 1024 * 1024 * 1.37); |
| 40 | +``` |
| 41 | + |
| 42 | +- Base64 adds ~37% overhead |
| 43 | +- 10MB file → ~13.7MB base64 |
| 44 | + |
| 45 | +## Design Decisions |
| 46 | + |
| 47 | +- **Never reject user uploads**: Compress instead of refusing |
| 48 | +- **Quality over size**: Try high quality first, only reduce if needed |
| 49 | +- **GIF exception**: GIFs skip compression (animation would be lost) |
0 commit comments