I have a situation where a file upload program that has been running without any problems for months now suddenly uploads completely empty files. When I turn on warnings (perl -w), I get a message like the following:

FATAL WARNING: read() on closed filehandle fh00001license.txt at CDownloadEditor.pm line 293.

Here is my main problem. I had this happen on a different server about a month ago. After a full day of scratching my head, delving back into the apache config, making slight modifications, looking at up2date logs, etc, I gave up for the day. When I tried using the program the next morning, it worked. So, I had made many different changes and rewritten the code many times with no change, it still did not work when I left, and it worked when I came back (no one else had access BTW). To further complicate things, my up2date logs showed that nothing changed overnight.

I thought of it as a weird fluke and shrugged it off, now it is happening on a different server. Both of these two servers are fairly small and the problem only affected one client. Now I'm concerned that this problem might occur on one of our critical servers that hosts many clients.

Another piece of important information is that while both the program that failed and then worked and this current program are programs to upload files, they are completely different versions of code that work differently. They don't even share any custom libraries in common.

The subroutine I call is as follows:

sub uploadFile
{
	my $self = shift;
	my %FORM = %{$self->{FORM}};
	my %sysvars = %{$self->{GVARS}};
	
	my ($variableName, $variableID);
	
	if(@_)
	{
		$variableName = shift;
		$variableID = shift;
	}
	
	return if($FORM{$variableName} eq '');
	
	my $extension = $1 if($FORM{$variableName} =~ /\.([^\.]+)$/);
	my $fileHandle = $FORM{$variableName};
	my $filename = $variableID . $variableName . '.' . lc($extension);
	my $path = "$sysvars{MODFILEPATH}/$sysvars{FILE}";
	
	if($filename =~ /([^\/\\]+)$/)
	{
		$filename = $1;
	}
	else
	{
		return "error";
	}
	
	if (!(-d "$path/"))
	{
		mkdir("$path/", 0777);
	}
	if(-f "$path/$filename")
	{
		unlink("$path/$filename");
	}
	
	if(!(open(IMAGEFILE, ">$path/$filename")))
	{
		if($FORM{'SCRIPTERROR-FILEUPLOAD'} eq '')
		{
			$self->{FORM}->{'SCRIPTERROR-FILEUPLOAD'} = 1;
			
			$self->setErrorMessage("File did not upload correctly. The system administrator has been notified of this issue.<br>Please try editing the record later to upload the file.");
			
			$self->sendTechnicalInformation("File permissions issue.");
		}
		
		return;
	}
	
	my $bytesRead;
	my $buffer;
	
	
	while(my $bytes = read($fileHandle, $buffer, 2096))
	{
		$bytesRead += $bytes;
		binmode IMAGEFILE;
		print IMAGEFILE $buffer;
	}
	
	close($fileHandle);
	close(IMAGEFILE);
	
	chmod (0777, "$path/$filename");
	
	$self->{FORM}->{$variableName} = "$sysvars{MODFILEURL}/$sysvars{FILE}/$filename";
	
	$self->doSQLUpdate('', "ID='$variableID'");
	
	$self->setSuccessMessage("Upload Successful: $bytesRead bytes read.");
}

No other code before this call attempts to open or close any file handles.

This subroutine is called by a line such as:

$self->uploadFile('file', $id) if($FORM{'file'});

Note that the %FORM hash contains variables passed in from the CGI module. The code that handles that is as follows:

my $query = new CGI;
	
	my $sql = do_sql->new;
	$sql->connect;
	
	foreach my $key (sort {$a <=> $b} $query->param())
	{
		my $value = $query->param($key);
		
		$self->{FORM}->{$key} = $value;
	}

If you have any advice to offer, please share it. This has baffled me long enough. Thank you.

Recommended Answers

All 9 Replies

Another piece of important information is that while both the program that failed and then worked and this current program are programs to upload files, they are completely different versions of code that work differently. They don't even share any custom libraries in common.

Almost sounds like someone is hitting the "stop" button on the browser while uploading a file. Maybe someone else will have an idea to help you, but I am baffled by the problem you have described.

Thanks for the response KevinADC. Unfortunately, it's nothing as simple as that. I run these tests myself using a variety of different browsers and with both small and large files with the same results. The Apache error logs don't report any problems either whereas they would typically report a short send by a connection failure or stop button.

just for sanity sake I would add in some error handling for any sytem functions, like these:

if (!(-d "$path/"))
	{
		mkdir("$path/", 0777) or some error handling here;
	}
	if(-f "$path/$filename")
	{
		unlink("$path/$filename") or some error handling here;
	}

make sure everything else is working so you can for sure track it down to the read() function being the real source of the problem, or at least the point where the problem occurs. Why the filehandle is closed is a mystery at this point. It worked, nothing got changed, now it does not work.... thats possible but leads to more fundamental questions, like is the server/computer going bad? Was new software installed or ugraded? It does not sound like a perl problem though.

A couple things. First, is it wise to have the binmode within the loop

while(my $bytes = read($fileHandle, $buffer, 2096))
	{
		$bytesRead += $bytes;
		binmode IMAGEFILE;
		print IMAGEFILE $buffer;
	}

I've always called it first after opening the file handle, but before operating on the file.

Second, the error refers to a module 'CDownloadEditor.pm line 293'. Is this code part of that module, or does the script call that module. What is the code around line 293, or is that line the call to this subroutine?

A couple things. First, is it wise to have the binmode within the loop

while(my $bytes = read($fileHandle, $buffer, 2096))
	{
		$bytesRead += $bytes;
		binmode IMAGEFILE;
		print IMAGEFILE $buffer;
	}

I've always called it first after opening the file handle, but before operating on the file.

Second, the error refers to a module 'CDownloadEditor.pm line 293'. Is this code part of that module, or does the script call that module. What is the code around line 293, or is that line the call to this subroutine?

Good point on the binmode in the loop. I've fixed that in other code, but it never caused any problems. That section of code doesn't matter however as the read() fails before the loop even executes.

As for CDownloadEditor.pm, that is the file that I have the large portion of code taken from. Line 293 is the while loop with the read() call (the beginning of your quoted code).

After bringing in an expert I know, a solution was found. The problem is that I use the CGI package to pull in all the form variable data. I let my CGI object variable go out of scope. Apparently, something either with my Perl packages or my system caused the CGI object to be garbage collected before my program could use the data.

After putting a copy of the object reference in a variable that had a top-level scope, the problem went away.

Thank you very much for your help KevinADC and trudge.

ahh, that will be a good thing to remember for future reference.

Just for my own curiosity, do you have

use strict;
use warnings;

at the top of your script? It may have caught something which would have solved this faster. It's always a good idea to include both those statements on production code.

ps. Please mark this as 'SOLVED' by editing your first post in this thread.

Just for my own curiosity, do you have

use strict;
use warnings;

at the top of your script? It may have caught something which would have solved this faster. It's always a good idea to include both those statements on production code.

ps. Please mark this as 'SOLVED' by editing your first post in this thread.

I did originally have "use strict". I added warnings when the problem occurred. The warning told me that I was trying to read a closed file handle. Helpful information, but that did not get me pointed in the right direction immediately.

BTW... I don't think it's possible to edit posts. I didn't know we could mark threads solved now, so after a bit of looking around I found a little link at the bottom of the page above the quick reply box for marking the thread solved. A very unintuitive and hard to find link if you ask me.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.