[Bug]: Image editor silently writes PNG data into .JPG files (uppercase extension breaks canvas.toBlob MIME)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript
Research direction
Start in src/components/ImageEditor.vue, especially defaultSavedImageType() and onSave(), then trace how the Save image as dialog handles the format field. Reproduce with an uppercase .JPG image and inspect the encoded blob type before upload. Done when saving preserves JPEG bytes for uppercase extensions and mismatched encoded data is not silently uploaded.
Written by the indexing model from the issue text.
Description
Bug description
Editing a photo whose extension is uppercase .JPG in the built-in viewer image
editor corrupts the file: the stored bytes become PNG while the filename keeps its
.JPG extension.
Observed on Nextcloud 32.0.14.1, viewer 5.0.0, Chrome 155:
| before | after "Speichern" (save) | |
|---|---|---|
| content type | image/jpeg |
image/png |
| size | 2 865 024 B | 504 955 B |
| magic bytes | FF D8 FF |
89 50 4E 47 |
file on the server: PNG image data, 4320 x 3240, 8-bit/color RGBA.
The upload itself succeeds (PUT /remote.php/dav/files/<user>/<path>.JPG → 204),
so there is no indication anything went wrong.
Steps to reproduce
- Upload a JPEG with an uppercase extension (e.g.
ABC1234.JPG— the default
export of many cameras). - Open it in the viewer (
/apps/files/...→ click the file), pressCtrl+E
(or open with?editing=true) so the image editor opens. - Apply any change (e.g. Filters → Black & White).
- Click Save → Save image as dialog appears → Save → confirm overwrite.
- Inspect the file server-side:
file data/<user>/files/ABC1234.JPG
# PNG image data, 4320 x 3240, 8-bit/color RGBA, non-interlaced
Expected behaviour
The file must still be a JPEG (or the user must be told the format changed).
Root cause
In src/components/ImageEditor.vue (viewer 5.0.0):
defaultSavedImageType() {
return extname(this.decodedSourceName).slice(1) || 'jpeg' // -> "JPG"
}
The extension is not lower-cased. The Filerobot editor then builds the MIME
type from it ('image/' + (ext === 'jpg' ? 'jpeg' : ext)) → image/JPG.
onSave() only normalises case-sensitively:
mimeType = mimeType.replace('jpg', 'jpeg') // no-op for "image/JPG"
...
const blob = await new Promise(resolve => imageCanvas.toBlob(resolve, mimeType, quality))
const response = await axios.put(putUrl, new File([blob], fullName))
HTMLCanvasElement.toBlob() falls back to image/png for an unknown MIME type
(per spec: "if type is not a supported image format, use image/png"), so PNG
bytes are uploaded under the original .JPG name. The File's name is also not
checked against blob.type.
Consequences seen in the wild:
- PNG files that are 5–15× larger than the JPEG they replace (a 15 MB PNG was
written over a 1.4 MB JPEG in a user report). - Metadata/EXIF is lost, so Memories date/time editing then fails with
Could not set exif data: Error: Not a valid JPG (looks more like a PNG)(HTTP 500). - Previews, sync clients and third-party apps see a file whose extension lies
about its content.
Suggested fix
defaultSavedImageType() {
return (extname(this.decodedSourceName).slice(1) || 'jpeg').toLowerCase()
}
plus a defensive normalisation in onSave() (case-insensitive jpg → jpeg)
and, most importantly, a check after encoding:
if (blob.type !== mimeType) { showError(...); return } // never upload mismatched bytes
Also worth lower-casing/validating the extension in the "Save image as" dialog
(format field), which currently keeps JPG as well.
Environment
- Nextcloud 32.0.14.1, PHP 8.3.33,
viewer5.0.0 (default install) - Chrome 155, Linux
- Affected files: any image with an uppercase
.JPG/.JPEG/.PNG-style extension;
lowercase.jpgis unaffected (image/jpegworks).
- Dominant language
- JavaScript
- Stars
- 121
- Forks
- 70
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 18
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from nextcloud/viewer
-
3. to review
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
nextcloud/viewer#3196 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
0. Needs triage bug
Difficulty 3/5 1-2 days Newbie friendliness 65/100
nextcloud/viewer#3343 · 2 comments ·
Maintainers usually reply within 1 day
-
0. Needs triage enhancement
Difficulty 3/5 1-2 days Newbie friendliness 65/100
Maintainers usually reply within 1 day
All issues in nextcloud/viewer
Similar issues
-
check:passed feeds:remove
Difficulty 1/5 Under an hour Newbie friendliness 65/100
iptv-org/database#37176 · 1 comment · 1 reaction ·
Maintainers usually reply within 9 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
hawk-digital-environments/HAWKI#443 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
feedback simulation workshop
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
githubnext/gh-aw-workshop#4370 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
AltimateAI/vscode-dbt-power-user#2089 ·