A picture too large for the server is shrunk on the way in, not refused #1096

Closed
opened 2026-10-05 05:46:00 +00:00 by claude-bot · 1 comment
Collaborator

Decided by the owner, Q156 (W19), 2026-10-05: (a) Shrink a too-large picture on the way in to the largest size taken, and store that.

Why

Since #1078 a picture is checked by its pixels before it is opened, and one over the bound is refused with a sentence. That now catches ordinary files:

  • a 24-megapixel phone photo (the default on recent iPhones);
  • a progressive JPEG over about 6.3 megapixels (photos saved again by an editor or a website often are);
  • a WebP over about 3.1 megapixels.

Nobody adding a picture to a wiki page wants 24 megapixels, and a refusal sends them off to resize it by hand.

Intended approach

  • JPEG first, where it is cheap. libjpeg can decode at 1/2, 1/4 or 1/8 scale while reading (Image.draft), so a large JPEG never has to be held at full size. A photo over the bound is decoded at the smallest scale that keeps it at or above the stored size, then resized to fit the bound.
  • Measure every case in a 512 MB container, as #1078 did: a 24 MP and a 48 MP baseline JPEG, a large progressive JPEG (does scaled decoding avoid the full coefficient buffer, or not?), CMYK, and a photo with a rotating tag (#1079). The worst accepted file must stay inside the existing envelope of about +62 MB.
  • A ceiling still applies. Past some size even a scaled decode does not fit; that is refused, with the sentence saying so. The ceiling comes from the measurements.
  • PNG and WebP: Pillow has no scaled decode for them. Find out by measurement whether anything cheap exists; if not, they stay refused over the bound, and the sentence says a JPEG may be larger.
  • Maps are not part of this. They are checked in the worker and already take 8192×6144.
  • Imports get the same behaviour: an over-bound page picture or attachment is shrunk where an upload would be, and left out and listed only where an upload would be refused.
  • Say what happened. The upload's answer and the page should not pretend the picture is the original: the stored width and height are the shrunk ones.
  • Everything #1078, #1086 and #1088 established still holds: metadata is removed before the open, the orientation is applied, the pixel check comes from the header.
  • An independent review before merging, as for the other changes to image_service.py.

Tests

  • A 24 MP JPEG is accepted and stored at or under the bound, upright, with no metadata.
  • A file past the ceiling is refused before any decode.
  • The scaled decode is what ran (the full-size decode is never reached for an over-bound JPEG).
  • Aspect ratio is kept to the pixel; a picture at or under the bound is stored exactly as it is today.

#1074, #1078, #1079, #1087.

**Decided by the owner, Q156 (W19), 2026-10-05: (a) Shrink a too-large picture on the way in to the largest size taken, and store that.** ## Why Since #1078 a picture is checked by its pixels before it is opened, and one over the bound is refused with a sentence. That now catches ordinary files: - a 24-megapixel phone photo (the default on recent iPhones); - a progressive JPEG over about 6.3 megapixels (photos saved again by an editor or a website often are); - a WebP over about 3.1 megapixels. Nobody adding a picture to a wiki page wants 24 megapixels, and a refusal sends them off to resize it by hand. ## Intended approach - **JPEG first, where it is cheap.** libjpeg can decode at 1/2, 1/4 or 1/8 scale while reading (`Image.draft`), so a large JPEG never has to be held at full size. A photo over the bound is decoded at the smallest scale that keeps it at or above the stored size, then resized to fit the bound. - **Measure every case in a 512 MB container**, as #1078 did: a 24 MP and a 48 MP baseline JPEG, a large progressive JPEG (does scaled decoding avoid the full coefficient buffer, or not?), CMYK, and a photo with a rotating tag (#1079). The worst accepted file must stay inside the existing envelope of about +62 MB. - **A ceiling still applies.** Past some size even a scaled decode does not fit; that is refused, with the sentence saying so. The ceiling comes from the measurements. - **PNG and WebP:** Pillow has no scaled decode for them. Find out by measurement whether anything cheap exists; if not, they stay refused over the bound, and the sentence says a JPEG may be larger. - **Maps are not part of this.** They are checked in the worker and already take 8192×6144. - **Imports** get the same behaviour: an over-bound page picture or attachment is shrunk where an upload would be, and left out and listed only where an upload would be refused. - **Say what happened.** The upload's answer and the page should not pretend the picture is the original: the stored width and height are the shrunk ones. - Everything #1078, #1086 and #1088 established still holds: metadata is removed before the open, the orientation is applied, the pixel check comes from the header. - An independent review before merging, as for the other changes to `image_service.py`. ## Tests - A 24 MP JPEG is accepted and stored at or under the bound, upright, with no metadata. - A file past the ceiling is refused before any decode. - The scaled decode is what ran (the full-size decode is never reached for an over-bound JPEG). - Aspect ratio is kept to the pixel; a picture at or under the bound is stored exactly as it is today. ## Related #1074, #1078, #1079, #1087.
rbrooks referenced this issue from a commit 2026-10-05 10:27:56 +00:00
Author
Collaborator

The owner answered Q161 on 2026-10-05 with (a): a photo just over the limit being stored at the largest of a half, a quarter or an eighth that fits (4160 × 3120 stored at 2080 × 1560) is fine for a wiki page; the backend's memory is not raised to 1 GB. It will be recorded as D288, in the same PR as #1105's decision. Nothing to change in the code.

The owner answered Q161 on 2026-10-05 with (a): a photo just over the limit being stored at the largest of a half, a quarter or an eighth that fits (4160 × 3120 stored at 2080 × 1560) is fine for a wiki page; the backend's memory is not raised to 1 GB. It will be recorded as D288, in the same PR as #1105's decision. Nothing to change in the code.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#1096
No description provided.