The problem is probably because Net::FTP just sends "@_" to the server, try this on $fn:
$fn =~ s/[^a-z0-9]/\\$1/g;
You might find that if you do that, and then a new Net::FTP gets installed, it will then be double-slashed out (which would be bad). You could just check the Net::FTP version before you do that regex there though, to make sure it needs to be done.
[edit] replaced $fh with $fn ;p