It appears that $ftp->put is supposed to return the remote filename created. Being a little pedantic, it seems the right thing to test for would be if the return value is defined. Suppose you "put" a file named '0'. Your current test would fail. Try defined($ftp->put($file)) or die(); Also print out the value of $! in your die statement, it might be helpful.
in reply to Accepted Data Connection "error" using put method of Net::FTP
Looking quickly at the source for Net::FTP, another thing you might try is using $ftp->put_unique instead of $ftp->put. It flows through some different logic where the return value is set a bit differently. Same goes for calling $ftp->binary before doing the put. Slightly different logic that could affect the return value.