Image file uploads suddenly became corrupt

i’ve looked around. i changed umask to 0022. so apologies if this feels repetitive.

according to my git logs i successfully pushed code July 30 2024 which allowed me to create directories and upload images, huzzah!

i don’t recall which 5.x version i had, nor do i recall when i updated to 6.x (doh) but I noticed this issue just a few days ago. I have since been trying to downgrade a bit and upgraded to 6.0.0.585 based on another post

i have two uploaders. a legacy Uploadifive that works fine
another i’ve rolled my own to replace Uploadifive

so I know it’s not the image itself.

i’m using NextJS, which posts to /api which forwards the request using Fetch to a remote Lucee server. the image is sent in the body. A file is saved, but when I go to view it directly I see “the image … cannot be displayed because it contains errors”

when i download the file (via FTP) it still does not open. i’ve cross checked various folder and file permissions against others that do work, so I’m ruling out a file permissions issue.

i’m a bit stumped and wondering if something changed on the lucee end, or maybe something on my end that i’m missing. looking for any suggestions

i’m considering rolling Lucee back as far as possible just to see.

LMK if you need more:

here’s some of the payload caught by the local api server before forwarding to lucee
(i know the ‘name’ says “files” but really it’s one file being submitted)

formData _FormData [FormData] {
  [Symbol(state)]: [
    {
      name: 'files',
      value: '"-----------------------------30263794752379993019815801375\\r\\nContent-Disposition: form-data; name=\\"files\\"; filename=\\"01_nation.png\\"\\r\\nContent-Type: image/png\\r\\n\\r\\n�PNG\\r\\n\\u001a\\n\\u0000\\u0000\\u0000\\rIHDR\\u0000\\u0000\\u0001\\u0018\\u0000\\u0000\\u0000s\\b\\u0006\\u0000\\u0000\\u0000C�\\u001d�\\u0000\\u0000\\u0000\\tpHYs\\u0000\\u0000\\u000b\\u0012\\u0000\\u0000\\u000b\\u0012\\u0001��~�\\u0000\\u0000\\u000ezIDATx��=���\\u001a�\\u001f]�E9��\\u000f@ڕH�N\\u000fRZ�\\u0001

here’s a dump of the response

{
	"catchDetail": { // catchDetail typically used for providing the error stack, or debugging like now
		"requestData": {
			"content": "LS0tL ... jg3LS0=",
			"headers": {
				"user-agent": "node",
				"content-length": "7094",
				"x-real-ip": "<redacted>",
				"x-webserver-context": "<redacted>",
				"x-tomcat-docroot": "<redacted>",
				"https": "on",
				"x-forwarded-proto": "https",
				"cookie": "<redacted>;<redacted>;<redacted>",
				"accept-language": "*",
				"x-forwarded-for": "<redacted>",
				"host": "<redacted>",
				"sec-fetch-mode": "cors",
				"accept": "*/*",
				"content-type": "multipart/form-data; boundary=---------------------------375777783422610373861269022283",
				"cache-control": "max-age=0",
				"accept-encoding": "br, gzip, deflate",
				"connection": "close",
				"x-modcfml-sharedkey": "<redacted>"
			},
			"protocol": "HTTP/1.0",
			"method": "POST"
		},
		"form": {
			"adminAction": "uploadMedia",
			"action": "admin",
			"uploadTo": "/Phone",
			"hostingCustomerId": "0"
		},
		"result": {
			"filewasrenamed": false,
			"attemptedserverfile": "01_nation.png",
			"serverfilename": "01_nation",
			"timelastmodified": "September, 24 2024 04:14:53 +0000",
			"fileexisted": false,
			"oldfilesize": 6747,
			"serverfileext": "png",
			"clientfilename": "01_nation",
			"filewasappended": false,
			"timecreated": "September, 24 2024 04:14:53 +0000",
			"filesize": 6747,
			"filewassaved": true,
			"clientfileext": "png",
			"contenttype": "image",
			"datelastaccessed": "September, 24 2024 04:14:53 +0000",
			"clientfile": "01_nation.png",
			"clientdirectory": "",
			"contentsubtype": "png",
			"filewasoverwritten": false,
			"serverdirectory": "<redacted>",
			"serverfile": "01_nation.png"
		}
	}
}

Don’t forget to tell us about your stack!
Nginx
OS: Ubuntu 18
Java Version: none?
Tomcat Version: idk
Lucee Version: 6.0.0.585

Oh, some CF might be useful:
current state, very similar to the previously known working state:

public function uploadMedia(){
            try{
                var local = structNew();
                local.requestData = GetHttpRequestData()
                // local.content = local.headers.Content;
                form.files = local.requestData.content;
                // local.files = local.content

                local.path = '#this.installPath#sites/#this.dsn#/site_images#this.form.uploadTo#'
                
                cffile(action='upload'
                    allowedExtensions=".png, .gif, .jpg, .jpeg, .svg, .webp, .heif, .avif"
                    destination='#local.path#'
                    result='result'
                    nameconflict='makeunique'
                    mode='777'
                    );

                local.imagePath = local.path & '/' & result.serverFileName & '.' & result.serverFileExt;
                fileSetAccessMode(local.imagePath, 777);

                // TODO: create thumbnails

                local.result = result;
                this.response.setSuccessDataAndMessage(local.result, 'files uploaded').setCatchDetail({
                    requestData: local.requestData,
                    result: result,
                    form: this.form,
                    local: local,
                });
            }catch(any e){
                var local = structNew()
                local.e = e;
                local.form = this.form
                local.headers = GetHttpRequestData()
                this.response.setSuccess(false)
                    .setMessage(e.message)
                    .setCatchDetail({
                        local: local,
                        catch: e,
                    })
            }   

And FWIW
The legacy upload handler.
Please don’t hate (too much) on my ~15/20-year old code =)

<cfcase value="site_images">
			<cfset pic_to_upload="#ListLast(listlast(filename,'/'), '\')#">
			<cfset pic_no_spaces=pic_to_upload>
			<cfset pic_no_spaces=strip_special_chars(pic_no_spaces)>
			<cfset pic_no_spaces=trim(pic_no_spaces)>
			<cfset pic_no_spaces="#replacenocase('#replacenocase('#replacenocase(pic_no_spaces, '.gif', '.gif', 'ALL')#', '.jpg', '.jpg', 'ALL')#', '.png', '.png', 'ALL')#">
			<cfif lcase(Right(pic_no_spaces,3)) eq 'jpg' or lcase(Right(pic_no_spaces,3)) eq 'gif' or lcase(Right(pic_no_spaces,3)) eq 'png' or lcase(Right(pic_no_spaces,4)) eq 'jpeg'>
				<cffile action="upload" 
				destination="#UWH_Install_Path#sites/#dsn#/site_images/temp.tmp" 
				filefield="FILEDATA" 
				MODE='777' 
				nameconflict="overwrite"> <!--- accept='image/gif,image/jpeg,image/pjpeg,image/png,image/x-png' --->
				<cfset real_file_name = file.clientfile>
				<cffile action='rename' source='#UWH_Install_Path#sites/#dsn#/site_images/temp.tmp' destination='#UWH_Install_Path#sites/#dsn#/site_images/#pic_no_spaces#'>
				<cfimage name="getImageVariables" action='read' source='#UWH_Install_Path#sites/#dsn#/site_images/#pic_no_spaces#'>
				<cfif getImageVariables['width'] gt 400 and resize_400 eq 1><cfimage action='resize' source='#UWH_Install_Path#sites/#dsn#/site_images/#pic_no_spaces#' destination='#UWH_Install_Path#sites/#dsn#/site_images/#replacenocase('#replacenocase('#replacenocase(pic_no_spaces, '.gif', '_400.gif', 'ALL')#', '.jpg', '_400.jpg', 'ALL')#', '.png', '_400.png', 'ALL')#' width='400' height='' quality='1'></cfif>
				<cfif getImageVariables['width'] gt 300 and resize_300 eq 1><cfimage action='resize' source='#UWH_Install_Path#sites/#dsn#/site_images/#pic_no_spaces#' destination='#UWH_Install_Path#sites/#dsn#/site_images/#replacenocase('#replacenocase('#replacenocase(pic_no_spaces, '.gif', '_300.gif', 'ALL')#', '.jpg', '_300.jpg', 'ALL')#', '.png', '_300.png', 'ALL')#' width='300' height='' quality='1'></cfif>
				<cfif getImageVariables['width'] gt 200 and resize_200 eq 1><cfimage action='resize' source='#UWH_Install_Path#sites/#dsn#/site_images/#pic_no_spaces#' destination='#UWH_Install_Path#sites/#dsn#/site_images/#replacenocase('#replacenocase('#replacenocase(pic_no_spaces, '.gif', '_200.gif', 'ALL')#', '.jpg', '_200.jpg', 'ALL')#', '.png', '_200.png', 'ALL')#' width='200' height='' quality='1'></cfif>
				<cfif getImageVariables['width'] gt 100 and resize_100 eq 1><cfimage action='resize' source='#UWH_Install_Path#sites/#dsn#/site_images/#pic_no_spaces#' destination='#UWH_Install_Path#sites/#dsn#/site_images/#replacenocase('#replacenocase('#replacenocase(pic_no_spaces, '.gif', '_100.gif', 'ALL')#', '.jpg', '_100.jpg', 'ALL')#', '.png', '_100.png', 'ALL')#' width='100' height='' quality='1'></cfif>
				<cfheader statuscode="200">
			<cfelse>
				<cfheader statuscode="500" statustext="Illegal file. Please only upload .JPG, .GIF or .PNG file types only.">
			</cfif>
		</cfcase>

So, CF to CF (lucee to lucee) works fine.

Something is awry going from FE → Node → CF/Lucee

I don’t know what changed, but I have gone a slightly different route.

The FE (including admin) is hosted on Vercel, but the database and other assets are hosted elsewhere.

So, upon file upload I’ll be saving the file locally (Vercel server, I hope) then creating another Fetch request sending the file to the other server.

I have a POC working locally, and will be updating the code to make it more dynamic and maintain the user’s uploaded file name.

Eventually I will likely add S3 or Vercel’s Blob support. I just haven’t gotten around to that w/ this project yet.

We can close this / mark as resolved.