Skip to content

[Bug]: Image editor silently writes PNG data into .JPG files (uppercase extension breaks canvas.toBlob MIME) #3424

Open

Description

@philippthiele

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

  1. Upload a JPEG with an uppercase extension (e.g. ABC1234.JPG — the default
    export of many cameras).
  2. Open it in the viewer (/apps/files/... → click the file), press Ctrl+E
    (or open with ?editing=true) so the image editor opens.
  3. Apply any change (e.g. Filters → Black & White).
  4. Click Save → Save image as dialog appears → Save → confirm overwrite.
  5. 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, viewer 5.0.0 (default install)
  • Chrome 155, Linux
  • Affected files: any image with an uppercase .JPG/.JPEG/.PNG-style extension;
    lowercase .jpg is unaffected (image/jpeg works).

Activity

  1. philippthiele commented on Oct 8, 2026

    @philippthiele
    Author

    Related, found while reproducing this on NC 32.0.14.1:

    This issue (#3424) is the one that corrupts the file: the upload itself succeeds, but with PNG bytes.

  2. aniruddh18122006-cyber commented on Oct 9, 2026

    @aniruddh18122006-cyber

    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?

  3. skjnldsv commented on Oct 9, 2026

    @skjnldsv
    Member

    @aniruddh18122006-cyber you don't need to ask, open a pr if you want :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions