Repository navigation
[Bug]: Image editor silently writes PNG data into .JPG files (uppercase extension breaks canvas.toBlob MIME) #3424
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-interlacedExpected 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 bytesAlso 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).
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTo triage
Activity
philippthiele commentedon Oct 8, 2026
Related, found while reproducing this on NC 32.0.14.1:
HEAD(browser cache revalidation withIf-None-Match) gets412instead of304from the vendored sabre/dav,fileExists()only accepts 200/404, so the save is aborted before any upload happens.This issue (#3424) is the one that corrupts the file: the upload itself succeeds, but with PNG bytes.
aniruddh18122006-cyber commentedon Oct 9, 2026
Hi! I’d like to work on this issue.
The uppercase extension appears to cause the MIME type mismatch: "JPG" is not normalized before constructing the MIME type, so "canvas.toBlob()" can fall back to PNG while the original filename remains unchanged.
I’d investigate normalizing the extension case-insensitively, then add a check that the encoded blob’s MIME type matches the expected format before uploading. I’d also look into regression tests for uppercase ".JPG" and ".JPEG" filenames to ensure the saved file’s content and extension stay consistent.
Would it be okay for me to work on this and submit a PR?
skjnldsv commentedon Oct 9, 2026
@aniruddh18122006-cyber you don't need to ask, open a pr if you want :)