Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I have a folder on my site that I allow users to upload files to. I monitor this folder with perl and want to rename the file once it is uploaded. It's easy enought to rename a file with perl, but once perl sees a new file in this folder it renames it before the upload is complete and this cancels the upload. I am using a windows 2000 system, and DOS rename will not allow until upload is complete. How do I get perl to behave the same way?

Replies are listed 'Best First'.
Re: Rename Uploaded Files
by demerphq (Chancellor) on Dec 02, 2002 at 18:41 UTC
    Two ways. First off try to acquire an exclusive lock on the file. If you can get one then the FTP service has finished writing. Second way, repeatedly stat the file until it is a certain age old. 5-10 minutes should be OK but not absolutely failsafe. Third way, do both. :-)

    Incidentally, the is no DOS (worth speaking of) on Win2k. Thank god. ;-) Normally we use the term 'shell' to refer to things like that. And i'm betting that the shell rename is waiting for a lock....

    use Fctl; my $canmove=0; MOVE:{ open my $fh,$filename or last MOVE; $canmove=flock $fh, LOCK_EX|LOCK_NB; close $fh; } if ($canmove) {
    Something like that should do the trick. (And yes people I am aware that this has holes if there are multiple processes running here. I am assuming this isn't the case, and that all he needs to know is that the MSFTP service has done with the file.)

    --- demerphq
    my friends call me, usually because I'm late....

Re: Rename Uploaded Files
by pg (Canon) on Dec 02, 2002 at 18:40 UTC
    I have two suggestions for you:
    • Your communication program should know when the upload is finished, and you can have your communication program to inform this 'rename process or thread' to rename a file has the specified file name.
    • You may not have control over your communication process, if it is purchased or for whatever reason, then you can monitor the size of the uploaded file for a certain amount of time, and only rename it after its size has been fixed/stable for a certain amount of time.
Re: Rename Uploaded Files
by bart (Canon) on Dec 02, 2002 at 19:08 UTC
    How are your users uploading files? You don't say.

    If it's with the file upload mechanism accessible from a form in a browser, then it's easy: make the script that handles the upload from the form, and I can only assume that's a Perl (maybe even a CGI) script, rename the file, after it got the whole file through.

Re: Rename Uploaded Files
by Anonymous Monk on Dec 02, 2002 at 19:05 UTC
    Two more alternatives. 1. There are win32 calls that will call back when filesystem events occur, such as fclose. This is a failsafe way of waking up when a file is uploaded and closed. 2. You can monitor the modified date. The modified date is updated after the fclose, and will be different from the creation date afetr the file is uploaded.
      Oooh. Good idea. I never thought of that one. :-) Thats getting implemented RSN.

      Thanks.

      --- demerphq
      my friends call me, usually because I'm late....

Re: Rename Uploaded Files
by Anonymous Monk on Dec 02, 2002 at 19:50 UTC
    Thank you all for your help so far. For now I have sttlled on something like:
    do{ $create = -C "test1.jpg"; $modify = -M "test1.jpg"; }until ($create == $modify and $create > 0); # Then process file or whatever.....
    I tried several things such as code I found here at the monestary to monitor the windows process list like so:
    use Win32::PerfLib; sub get_remote_process_list { my $server = $_[0]; my %rtasks; my %counter; Win32::PerfLib::GetCounterNames($server, \%counter); my %r_counter = map { $counter{$_} => $_ } keys %counter; my $process_obj = $r_counter{Process}; my $process_id = $r_counter{'ID Process'}; my $perflib = new Win32::PerfLib($server); my $proc_ref = {}; $perflib->GetObjectList($process_obj, $proc_ref); $perflib->Close(); my $instance_ref = $proc_ref->{Objects}->{$process_obj}->{Instance +s}; foreach my $p (sort keys %{$instance_ref}){ my $counter_ref = $instance_ref->{$p}->{Counters}; foreach my $i (keys %{$counter_ref}){ if($counter_ref->{$i}->{CounterNameTitleIndex} == $process +_id){ print "$instance_ref->{$p}->{Name} "; $rtasks{$counter_ref->{$i}->{Counter}} = $instance_ref +->{$p}->{Name}; } } } return %rtasks; }
    But I never saw a new Process appear in the list when someone was uploading so I had no idea what to monitor. Btw, I am testing this on my local network using WSFTP to send a file to a second computer. I also tried something like:
    use Win32::Process; use Win32; sub ErrorReport{ print Win32::FormatMessage( Win32::GetLastError() ); } Win32::Process::Create($ProcessObj, "C:/WINNT/system32/CMD.EXE", "dir", # p +rint dir just to see if I can do anything at all 0, CREATE_NEW_CONSOLE, ".")|| die ErrorReport(); $ProcessObj->Wait(INFINITE);
    because the shell acting like I wanted as far as renaming went, but I never could sent any commands to the new "Dos" window. frustrating......
      do{ $create = -C "test1.jpg"; $modify = -M "test1.jpg"; }until ...
      My CPU usage is spiking, just looking at that. Put a sleep 1; inside the loop to cool it off.
Re: Rename Uploaded Files
by nedals (Deacon) on Dec 02, 2002 at 19:44 UTC
    One other thought.

    If your script knows how to rename the file, then it probably knows the new name at the time the file is uploaded. So simply save the file with that new name and forget the rename process.

Re: Rename Uploaded Files
by yodabjorn (Monk) on Dec 02, 2002 at 23:49 UTC
    I have doen similar things in perl, but instead i used fam to communicate whats up with file and directory changes.

    There just happens to be a handy perl module for interfacing to fam here: SGI-FAM

    From the man page:
    fam is a server that tracks changes to the filesystem and relays these changes to interested applications. Applica- tions such as fm(1G) and mailbox(1) present an up-to-date view of the filesystem. In the absence of fam, these applications and others like them are forced to poll the filesystem to detect changes. fam is more efficient. Applications can request fam to monitor any files or directories in any filesystem. When fam detects changes to monitored files, it notifies the appropriate applica- tion.
    definatly less of a burden on your box ;)

    DOH! didn't see that this was on win32

    An intellectual is someone whose mind watches itself.
    - Albert Camus
Re: Rename Uploaded Files
by Theseus (Pilgrim) on Dec 02, 2002 at 23:31 UTC
    Or, what seems to be simpler to me(although untested and probably not the best way):
    while (-e $file_to_be_renamed) { system("rename $file_to_be_renamed $desired_name"); sleep 1; }