Skip to main content
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Awdhesh Kumar
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Awdhesh Kumar

Ask Ben: Limit File Upload Size In ColdFusion

By
Published in , Comments (49)

My client has a job inquiry form in which the web user can upload their resume for a job application. These resumes are then emailed to our contact in the human resources department. Since these files are getting attached via email, the client does not want the resumes to be very big. How can I limit the file size of the resumes to be no more than 50 KB?

Putting limitations on a file upload in ColdFusion is a bit tricky. The problems with something like that is that we can't really figure out how large the file is until it is fully uploaded (at least as far as I know - I have never seen a good upload progress bar in ColdFusion). You can do hacks using Javascript to get a File System Object, but those are a huge security risks and will prompt the user for approval. In order to make the processing as transparent as possible, we are going to have to handle it all on the server side.

However, having large files on the server is not usually a problem - it's the emailing of large files that can cause issues (email rejection and inbox size limitations I assume). Therefore, we are not going to try and do anything tricky, just simple server-side size validation.

When it comes to form interfaces and file manipulation of any kind, I like to handle the file validation very last. File manipulation has its own costs that include extra CFTry / CFCatch logic and heavier processing. Therefore, I don't like to do any file validation until I am sure that NOTHING else in the form will cause the form to not be valid. For instance, if someone's email is not valid and the form has to redisplay, I don't want to even bother checking the file as I know that the file cannot be used (since the form must refresh anyway).

Once you do know that you can upload and validate a file, it is very important to use CFTry / CFCatch blocks. There are any number of things that can cause exceptions to be thrown when dealing with an outside system and especially when that system is a file system. The idea is that we never want to show the end user an error if we can recover from it.

Take a look at how I am handling the file size validation in this form. I have tried to recreate a very minimal form that sounds reflective of the one you have built (a resume submission form in ColdFusion):

<!--- Kill extra output. --->
<cfsilent>

	<!--- Param the form variables. --->
	<cfparam name="FORM.name" type="string" default="" />
	<cfparam name="FORM.email" type="string" default="" />
	<cfparam name="FORM.resume" type="string" default="" />
	<cfparam name="FORM.submitted" type="string" default="0" />


	<!---
		With this array we are going to keep
		track of form validation errors.
	--->
	<cfset REQUEST.Errors = ArrayNew( 1 ) />


	<!---
		Check to see if the form has been submitted.
		No sense in doing any processing until the
		form gets submitted. Use the Val() method here
		because are submitted variable is a string
		and there is no garuuntee that it will be a
		numeric value (if someone messed with the form).
	--->
	<cfif Val( FORM.submitted )>

		<!---
			Now that the form has been submitted, we want
			to validate the data before we process it. Of
			all the validation, checking the file size is
			going to be the most costly as it requires
			actually requires file manipulation. Do that
			last and do that ONLY if no other form errors
			exist.
		--->
		<cfif NOT Len( FORM.name )>

			<cfset ArrayAppend(
				REQUEST.Errors,
				"Please enter your name."
				) />

		</cfif>

		<cfif NOT IsValid( "email", FORM.email )>

			<cfset ArrayAppend(
				REQUEST.Errors,
				"Please enter a valid email address."
				) />

		</cfif>

		<cfif NOT Len( FORM.resume )>

			<cfset ArrayAppend(
				REQUEST.Errors,
				"Please select a file to upload."
				) />

		</cfif>


		<!---
			Now that we have processed all the non-file parts
			of the form, let's check to see if there are any
			errors. If so, then there's not point in processing
			the file.
		--->
		<cfif NOT ArrayLen( REQUEST.Errors )>

			<!---
				Upload the file to the temp directory. Any
				time you work with file manipulation use a
				try / catch block as you never know what
				might go wrong.
			--->
			<cftry>

				<cffile
					action="UPLOAD"
					destination="#ExpandPath( './temp/' )#"
					filefield="resume"
					nameconflict="MAKEUNIQUE"
					/>


				<!---
					Once the file gets uploaded, we should have
					access to the file properties via the CFFILE
					object. Let's check the size to see if it's
					too big. Remember that we know the BYTE size
					of the file. To make things easier to read,
					I generally do file size using math. This
					way I can more easily see what I am dealing
					with. For this, we are limiting it to 50K
					which is 50 * 1024 bytes.
				--->
				<cfif (CFFILE.FileSize GT (50 * 1024))>

					<!--- Add our error message. --->
					<cfset ArrayAppend(
						REQUEST.Errors,
						"Your resume cannot be more than 50K."
						) />


					<!---
						We have no use for this file. Let's try
						to delete it from the file system.
					--->
					<cftry>

						<cffile
							action="DELETE"
							file="#ExpandPath( './temp/#CFFILE.ServerFile#' )#"
							/>

						<cfcatch>
							<!---
								There was an error deleting
								the file. You can log this,
								but it's not worth crashing
								the page over.
							--->
						</cfcatch>
					</cftry>

				</cfif>


				<cfcatch>

					<!---
						If there was any error, then alert the
						user. Also, since this error may have
						happened after the file upload, let's
						try to delete the file. Of course, this
						requires a try/catch if for no other
						reason, we don't know if the file was
						uploaded.
					--->

					<!--- Add our error message. --->
					<cfset ArrayAppend(
						REQUEST.Errors,
						"There was a problem uploading your resume."
						) />

					<!--- Try to delete the file. --->
					<cftry>

						<cffile
							action="DELETE"
							file="#ExpandPath( './temp/#CFFILE.ServerFile#' )#"
							/>

						<cfcatch>
							<!---
								There was an error deleting
								the file. You can log this,
								but it's not worth crashing
								the page over.
							--->
						</cfcatch>
					</cftry>

				</cfcatch>

			</cftry>

		</cfif>


		<!---
			Now that we are done validating the form
			data, we can process the form. Check to
			see if we have any form errors.
		--->
		<cfif NOT ArrayLen( REQUEST.Errors )>

			<!---
				ASSERT: At this point, we know that we do NOT
				have any form errors. We also know that we
				have a file ALREADY uploaded whose information
				is available in the CFFILE variable:

				CFFILE.ServerFile (name + ext)
				CFFILE.ServerFileName
				CFFILE.ServerFileExt
			--->

			<!--- Process FORM. --->

		</cfif>

	</cfif>


	<!---
		Before we render the content, let's clear the
		content buffer to minimize pre-page white space
		and tell the browser what kind of content to expect.
	--->
	<cfcontent
		type="text/html"
		reset="true"
		/>

</cfsilent>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
	<title>Limit File Upoad Size</title>

	<style type="text/css">

		body {
			font: 11px verdana ;
			}

		label {
			display: block ;
			margin-bottom: 16px ;
			}

		div.formerrors {
			color: #990000 ;
			}

	</style>
</head>
<body>


	<cfoutput>

		<!---
			Check to see if we have any form errors to
			display. We don't really care if the form has
			been submitted or not since it won't have any
			errors prior to submission.
		--->
		<cfif ArrayLen( REQUEST.Errors )>

			<div class="formerrors">

				<h4>
					Please review the following issues:
				</h4>

				<ul>
					<cfloop
						index="intError"
						from="1"
						to="#ArrayLen( REQUEST.Errors )#"
						step="1">

						<li>
							#REQUEST.Errors[ intError ]#
						</li>

					</cfloop>
				</ul>

			</div>

		</cfif>


		<form
			action="#CGI.script_name#"
			method="post"
			enctype="multipart/form-data">

			<!---
				This hidden form field will alert us that
				the form has been posted.
			--->
			<input
				type="hidden"
				name="submitted"
				value="1"
				/>


			<label>
				Name:
				<input
					type="text"
					name="name"
					value="#FORM.name#"
					size="30" />
			</label>

			<label>
				Email:
				<input
					type="text"
					name="email"
					value="#FORM.email#"
					size="30" />
			</label>

			<label>
				Resume:
				<input
					type="file"
					name="resume"
					value=""
					size="60" />
			</label>

			<input type="submit" value="Upload Resume" />

		</form>

	</cfoutput>

</body>
</html>

Notice that the file handling and manipulation code is fairly verbose. I am not sure if what I am showing you here is the best way to do things, but it works quite well for me. This method should leave minimal garbage on your server as it attempts to delete the uploaded file if it is too big or if an error was thrown. Of course, I have not put in any form processing. It sounds like you have that process under control. Hope that helps.

Want to use code from this post? Check out the license.

Reader Comments

29 Comments

Just to expand on this? My requirement is upload files only of a specific mime-types, and the most tricky one is .msg (Outlook file). However, since .msg is not a standard file type (not a registered mime type), the server is assigning it with the mime type "application/octet-stream" by default. The problem is that this mime type is the same mime for .dll, .exe, etc..., which should be prohibited. Any take on this?

-Dmitriy

15,883 Comments

Honestly, I would handle it in the exact same way. I used to use the EXCEPT attribute of the CFFILE, but that never worked out the way I wanted it to since built-in mime-types are not consistent (at best).

You might be able to check the file extension from the form field (don't know off hand):

ListLast( FORM.resume, "." )

But that may be "tmp" for the temp file that gets created for the upload. If that is the case, just upload the file, and check the file extension of the uploaded file:

ListFind( "exe,bat,ini", CFFILE.ServerFileExt )

If it's a bad file extension, delete and report the error. To me, this gives you the most flexability and will have the most consistent outcome.

Now, this of course is geared towards a Windows machine that uses file extensions. If you go to Mac or Linux or something where file extensions are not really used, I have no idea what looks like. No recommendations there. But the above method has worked fine for me over the years.

8 Comments

Ben, it's the ACCEPT attribute, and it would only list accepted mime types. I forgot where I saw it, but I know someone has a mime type structure built with most of the common types already.

15,883 Comments

Ooops, sorry about the misspelling (shows how often I use it). The problem is that no mime-types are not very consistent. Look at what Dmitriy is saying above - he wants to get a non-standard mime type. Since the server doesn't know this one, he passes through the octet-stream. This is like the catch-all of mime types. I think if you put that in the accept attribute it will actually let anything through - BUT please do not quote me on that as I have not tested it or used it in a long time.

But in my experience, I have even had problems where image files don't have proper mime-types and they get rejected and then we have to open them up in a graphics editor and re-save them.

All in all, checking the mime type just feels less reliable and flexible.

29 Comments

Thanks, however this is not what I need. The system is much more sophisticated than that. First of all, extension check is not acceptable, and second of all, you can get an exe file rename it as a txt; however when you upload it it's still an exe.

14 Comments

Just thinking out of the box, you could also consider using a web based rich text editor. This gives you a little more control on the client side for size limitation and eliminates issues of someone uploading different file formats, macro viruses and such. Some RTE's also supports word paste otpions.

15,883 Comments

@Dmitriy,

While you can rename an EXE to be TXT, doing so (at least on a window's machine) will not allow it to execute as an EXE when double clicked or invoked from a URL (not sure what CFExecute would do). Plus, there is the issue of "worst case scenario" - if someone did upload an EXE as a TXT, what would happen? Would the EXE ever be executed? What happens to the TXT file? What is the worst that could happen? Is this any different than someone just uploading a corrupted, unusable TXT file?

I guess it becomes a trade-off. Checking the file extension gives you more control over file type acceptance, but does not automatically reject based on mime-type. Doing a mime-type acceptance check might reject falsely.

Perhaps a combo of the two would be best. Check the mime type (after upload); if is a bad mime time (ex. EXE, BAT) then reject. If it is undeterminable (ex. octet-stream), then check the file extension.

4 Comments

Instead of doing <cffile action="upload" ...> then comparing the file size & if it doesnt matches removing it

you can also use
<cfset f = createObject("java","java.io.File").init(form.resume)>
<cfif f.length() lT (50 * 1024)>
<cffile action="upload" destination="location">
</cfif>

The point is that <cffile action="upload"> does nothing but renames the file to the location you specified in the destination attribute.
The file will be uploaded in a temp CF directory before <cffile> comes into play. So one can directly refer the file at its original location to check the size & if it matches can move on & put in the destination location.
Since its a temp location where the file gets uploaded one doesnt need to explcitly delete the file unless you have space limitations.

39 Comments

haven't used this yet myself, but if you the customer is cool with requiring users to have at least Flash 8, cf_flashUpload is supposed to be sweet: http://www.asfusion.com/blog/entry/file-upload-with-coldfusion-flash-forms

http://www.asfusion.com/examples/item/file-upload-with-coldFusion-flash-forms

and then in addition to the file types checking, you'll want to make sure and prevent huge (definition of huge is up to you, but I hear much past 500Mg becomes a problem) files from getting all the way through with the following setting: "Maximum size of post data" - see http://www.coldfusionmuse.com/index.cfm/2006/5/24/Limit.Post

20 Comments

Unfortunately flash forms aren't really accessible, have poor fallback, and can be a real PITA for users.

A great solution to providing a better user experience, in terms of file uploading, that I've found is SWFUpload.

http://swfupload.mammon.se/

Works really nicely. Falls back to a regular file input if the user doesn't have JS or flash enabled. Lets you write JS handlers to bind to events for actions in the client code. Quite awesome.

15,883 Comments

Hmm, that looks really slick (swfupload). I just tried the demo and it is very user friendly. I will look into this more thoroughly. Thanks for the link.

4 Comments

I've use cgi.content_length to check the length before the item it uploaded, is there a downside to doing it this way that I am not aware of?

15,883 Comments

@Mark,

Not sure what CGI.content_length has in it if you have multiple files being posted. The nice thing about going through CFFILE is that you can do content length checks per-file.

1 Comments

Ben is there anyway after the upload has past all the validation and the resume has been uploaded to redirect the user to a different page

15,883 Comments

@Anthony,

Yeah. After all the form processing is done, just execute a CFLocation tag to send the user on their way to another page.

2 Comments

Hi,

Is it possible to make the upload resume non mandatory? When i try to modify the code it throws up all kinds of errors.
a sample of the code needed would be great !

thanks !

15,883 Comments

@Liam,

To make it non-mandatory, first, you have to remove the data validation CFIF statement:

<cfif NOT Len( FORM.resume )> .. </cfif>

Then, in the form processing area, rather than just automatically uploading and using the file, you have to check to see if has been uploaded:

<cfif Len( FORM.resume )> ... </cfif>

2 Comments

Hi Ben thanks for the information.
As I am new to Coldfusion, I was wondering if you would explain a few things more for me.

1. do I just cut the code for the <cfif NOT Len( FORM.resume )> down to the end tag and change the <cfif NOT Len( FORM.resume )> to <cfif Len( FORM.resume )> and leave the rest of the code the same?

2. Where in the form processing section would i now place this cut code?

I have tried commenting out the <cfif NOT Len( FORM.resume )> part and it gets to a point where it needs to process the CFFILE.ServerDirectory & "\" & part and because there is no file attached therefore a directory path it errors out.

thanks for your help again !

Liam.

15,883 Comments

@Liam,

Take out the initial CFIF/CFIF statement for the Len() of the file field. Then, change this CFIF statement:

<!---
Now that we have processed all the non-file parts
of the form, let's check to see if there are any
errors. If so, then there's not point in processing
the file.
--->
<cfif NOT ArrayLen( REQUEST.Errors )>

... to :

<cfif (
(NOT ArrayLen( REQUEST.Errors )) AND
Len( FORM.resume )
)>

This way, you will upload the file if there are no form errors AND the file has been selected.

4 Comments

Hi Ben,
Thought I should my point here, well i was thing why upload a file and then delete, isn't it obivous if we check the file size before uploading like this below:

<cfif VAL(CGI.CONTENT_LENGTH) GT 50000>
<cfset msg = 'File Too Large'>
<cfelse>
Upload Code goes here
</cfif>

15,883 Comments

@Matt,

I think the file has to be uploaded as part of the request before the CGI scope is even available. Plus, I like doing things per-file since the content_length is the total, not broken up in any way.

132 Comments

@Adrian

That won't work because of the possible race condition. Right after the fileExists() and before the delete the file could vanish, and then the operation would still fail.

3 Comments

Just a thought. There is a nice GNU-Util you can use called "file" that is shipped with linux but also is available for windows.

What it does is check the contents of a file to determine its type.

This means you can identify files from their contents rather than an extension which is easily faked.

so

upload file
check with file util
delete unwanted files
scan if its a valid file with av
make sure to pass back any upload files through your own handler and store them in a folder not accessible via the web (just in-case your web server allows execution)

voila!

15,883 Comments

@Andrew,

I am not sure how this is different from using ColdFusion to check the file size? Are you saying this happens before the request gets passed to the ColdFusion application?

3 Comments

This is after the upload of the file.

The idea is to do Content Inspection to determine the MIME type of the file and this is something GNU file does really well.

The internal Coldfusion file information stuff should be fine but you mentioned previously that the mime-type stuff was not so great. I find GNU file to be really reliable.

It also has a config file for identifying mime-types.

9 Comments

Hi Ben

Is there way to check for the file extensions before I actually upload the file. (No Javascript)

9 Comments

I like the post where the the java was used and thought maybe there is one to get a file extension

<cfset f = createObject("java","java.io.File").init(form.name)>

15,883 Comments

@Erik,

Ah sorry, I think I misunderstood what you were saying since you mentioned that you need to do this without Javascript.

By the time you get to ColdFusion, the file has *already* been uploaded to the server. It gets uploaded into a TEMP directory. When you invoke the CFFILE/Upload action, all you are really doing is moving the file form the TEMP directory into the Destination directory.

You might want to check out David Boyer's post on hidden FORM values:

http://misterdai.wordpress.com/2010/06/23/form-scope-hidden-upload-details/

There's a lot you can do behind the scenes.

4 Comments

We might include checking the file size if zero, that will cause the file upload error.
Try saving a text file with no content on it and upload.

15,883 Comments

@Nelgraine,

Is that true? I am not sure I've actually tried to save a file that is empty. Although, I believe the CFZip tag will error if you try to work with zero-length archives. I'll have to test that out at some point.

21 Comments

I quite like Rahul Narula's way of using Java to view the file size before upload. It definitely beats uploading the whole file(s) and then erroring to the user. Imagine an image upload form that limits users to 50K files and they upload 10 x 5MB files. They would have to wait for 50MB to upload before receiving an error, and that's if the request didn't timeout before that.

Anyone used Rahul's method before? Definitely going to copy that code to file for future use (after testing of course)

2 Comments

Ben,

You can always use SESSION variables to store POST variables. In the advent of an error, re-display form values using your session variables. That way the end user doesn't need to re-type anything anyways.

In PHP, there is a setback though. If you go over the post maximum size which is a lot, it wipes out POST completely. If that happens, no values are returned to the form with your session (the session the you would have assigned from post). But you would have to exceed the file upload size.

Regards,
Ravi.

1 Comments

Ben,

Do you know of any way to override the "Maximum size of post data"
for a particular app or session? We had this set to 50 MB, but we had advertised to our faculty that they could upload videos up to 75 MB.
I asked for our CF administrator to update our "Maximum size of post data" setting to 75 MB. She did, but she would like to only open it up for this app - since the others should never post this much data.

Thanks,
Sue

6 Comments

recently I uploaded two files, the reported file size in windows explorer was 1097 kb for one file and 1853 kb for the other. [2950 kb]

However the cgi.content_length variable was reporting the size as 3019924.

I want to limit total file upload to 10 megabytes so what is the proper cgi.content length to represent this?

23 Comments

I just had a Book from "Brooks" and in his book, "Programming Coldfusion MX 2nd Edition, this is clearly mentioned as:

"A message is written back to the browser letting the user know that the file was too big to upload, and processing of the ColdFusion template is halted before the file can be uploaded. If, however, the value of CGI.content_length is less than or equal to 100,000, the template continues processing uninterrupted."

Acc to him, the file will not be uploaded if the size is gt than matched.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel