Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [perl] pasek postępu uploadu plików
Forum PHP.pl > Forum > Serwery WWW
yarek12
Witam,
Już od dłuższego czasu próbuję napisać pasek postępu, przepatrzyłem praktycznie cały internet i wszystkie możliwości, jednak nie znalazłem żadnego działającego rozwiązania.

Więc postanowiłem spróbować z perlem, oto mój kod uploadu:

  1. #!/usr/bin/perl -w
  2.  
  3. use strict;
  4.  
  5. use warnings;
  6. use CGI;
  7. use CGI::Carp qw(fatalsToBrowser);
  8.  
  9. my $q = new CGI();
  10.  
  11.  
  12. # Handle actions.
  13. if ($q->param('action') eq "upload") {
  14. # They just submitted the form and are sending a file.
  15.  
  16. my $filename = $q->param('file');
  17. my $handle = $q->upload('file');
  18. $filename =~ s/(?:\\|\/)([^\\\/]+)$/$1/g;
  19.  
  20. # File size.
  21.  
  22. my $size = (-s $handle);
  23.  
  24. # This session ID would be randomly generated for real.
  25. my $sessid = 'my-session';
  26.  
  27. # Create the session file.
  28.  
  29. open (CREATE, ">./sessions/$sessid") or die "can't create session: $!";
  30. print CREATE "size=$size&file=$filename";
  31. close (CREATE);
  32.  
  33. # Start receiving the file.
  34.  
  35. open (FILE, ">./files/$filename");
  36. while (<$handle>) {
  37. }
  38. close (FILE);
  39.  
  40. # Delete the session.
  41.  
  42. unlink("./sessions/$sessid");
  43.  
  44. # Done.
  45. print $q->header();
  46. print "Thank you for your file. <a href=\"files/$filename\">Here it is again</a>.";
  47. }
  48.  
  49. elsif ($q->param('action') eq "progress") {
  50. # They're checking up on their progress; get their sess ID.
  51. my $sessid = $q->param('session') || 'my-session';
  52. print $q->header(type => 'text/plain');
  53.  
  54. # Does it exist?
  55.  
  56. if (!-f "./sessions/$sessid") {
  57. print "error:Your session was not found.";
  58. exit(0);
  59. }
  60.  
  61. # Read it.
  62.  
  63. open (READ, "./sessions/$sessid");
  64. my $line = <READ>;
  65. close (READ);
  66.  
  67. # Get their file size and name.
  68.  
  69. my ($size,$name) = $line =~ /^size=(\d+)&file=(.+?)$/;
  70.  
  71. # How much was downloaded?
  72.  
  73. my $downloaded = -s "./files/$name";
  74.  
  75. # Calculate a percentage.
  76. my $percent = 0;
  77. if ($size > 0) {
  78. $percent = ($downloaded / $size) * 100;
  79. $percent =~ s/\.(\d)\d+$/.$1/g;
  80. }
  81.  
  82. # Print some data for the JS.
  83.  
  84. print "okay:size=$size&received=$downloaded&percent=$percent";
  85. exit(0);
  86. }
  87. else {
  88. die "unknown action";
  89. }


Serwer zwraca jednak następujący błąd: couldn't create child process: 720003: upload.cgi


Z góry dziękuję za pomoc, bardzo mi na tym zależy, ewentualnie jeśli ktoś zna inny sposób na pasek postępu, również będę wdzięczny.
Pozdrawiam.
JohnnyB
właśnie niedawno zrobiłem sobie fajny paseczek, tylko nie w Perlu, a z wykorzystaniem APC - działa bez zarzutu, jedyny kłopot, że trzeba mieć APC na serwerze.
yarek12
Próbowałem APC, jednak nie działało, możesz mi coś poradzić? Jak się do tego zabrać, może znasz jakiś tutorial?
JohnnyB
sprawdź jakie masz ustawienia (w plikach konfiguracyjnych lub za pomocą phpinfo() ).
Interesuje nas sekcja APC, w szczególności ustawienia dotyczące opcji rfc1867. Poniżej podaję wartości jakie są u mnie:

apc.enabled On

apc.rfc1867 On
apc.rfc1867_freq 0
apc.rfc1867_name APC_UPLOAD_PROGRESS
apc.rfc1867_prefix upload_

sprawdź i napisz co jest u ciebie, będzie to później potrzebne.
yarek12
apc.enabled On On
apc.rfc1867 On On
apc.rfc1867_freq 0 0
apc.rfc1867_name APC_UPLOAD_PROGRESS APC_UPLOAD_PROGRESS
apc.rfc1867_prefix upload_ upload_

Widać ustawienia mamy takie same.
JohnnyB
ok, no to lecimy dalej:
  1. <?php
  2. if(isset($_GET['progress'])) {
  3. header('Expires: Tue, 08 Oct 1991 00:00:00 GMT');
  4. header('Cache-Control: no-cache, must-revalidate');
  5. $status=apc_fetch("upload_$_GET[progress]");
  6. if(!$status) die(0);
  7. echo round($status['current']/$status['total']*100);
  8. exit();
  9. }

to właśnie sedno programu - procent przesłania sprawdzamy za pomocą funkcji apc_fetch, jako musimy podać nazwę naszego uploadu - w powyższym przykładzie będzie ona przesyłana poprzez GET w zmiennej o nazwie 'progress'. Nazwa musi być poprzedzona przedrostkiem zdefiniowanym w apc.rfc1867_prefix (u nas 'upload_'). Zwracaną wartość będziemy okresowo pobierać poprzez AJAX.

  1. $message='';
  2. if($_FILES) {
  3. if($_FILES['plik']['error']==UPLOAD_ERR_OK) {
  4. $path='/tmp/';
  5. $path.=basename($_FILES['plik']['name']);
  6. if(move_uploaded_file($_FILES['plik']['tmp_name'],$path))
  7. $message='Plik przesłany poprawnie.';
  8. else
  9. $message='Błąd podczas kopiowania pliku';
  10. } else $message="Błąd podczas przesyłania pliku: ".$_FILES['plik']['error'];
  11. die($message);
  12. }
  13. ?>

To klasyczny fragment obsługujący przesyłanie pliku.

  1. <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
  2. <script type='text/javascript'>
  3. var oReq=new XMLHttpRequest();
  4.  
  5. function getProgress() {
  6. oReq.open('GET','test.php?progress=pasek_1',true);
  7. oReq.onreadystatechange=updateProgress
  8. oReq.send(null);
  9. }
  10.  
  11. function updateProgress() {
  12. if(oReq.readyState!=4 || oReq.status!=200) return;
  13. var progress=oReq.responseText;
  14. document.getElementById('procent').innerHTML=progress+'%';
  15. if(progress<100) setTimeout('getProgress()',500);
  16. }
  17. </head>

Pierwsza funkcja pobiera procent wysłania pliku przez AJAX, druga ustawia na tej podstawie zawartość diva.

  1. <?php echo $message; ?>
  2. <div id='procent'></div>
  3. <form name='f1' method='POST' action='' enctype='multipart/form-data'
  4. onSubmit='setTimeout("getProgress()",500)' target='upload_frame'>
  5. <input type='hidden' name='APC_UPLOAD_PROGRESS' value='pasek_1' />
  6. Plik: <input type='file' name='plik' /><br>
  7. <input type='submit' value='Wyślij' />
  8. </form>
  9.  
  10. <a href=''>Odśwież</a>
  11. <iframe style='display:block' name='upload_frame'></iframe>
  12. </body>
  13. </html>

Na koniec właściwa część do wysłania pliku - jak widać tworzymy specjalne pole o nazwie określonej w apc.rfc1867_name (APC_UPLOAD_PROGRESS) i ustawiamy dowolną wartość - to co tu wpiszemy musimy potem przesłać do funkcji apc_fetch.

To wszystko - wszystkie kody mogą być w jednym pliku, w moim przypadku nazywa się on test.php (ta nazwa używana w wywołaniu AJAX-owym). W przykładzie wprawdzie postęp jest podawany liczbowo, ale myślę, że dorobienie do tego paska nie będzie dużym problemem.
Aha, warto jeszcze pamiętać o ustawieniu odpowiednio dużej wartości post_max_size w php.ini.
yarek12
Chyba coś robię nie tak, apc widać działa bo żaden błąd nie wyskoczył, efekt jest taki że nie uploaduje nawet żadnego pliku, przeładowuje tylko stronę a do ramki wczytują całą stronę od nowa.
Nie wiem czy dobrze zrozumiałem kod, zobacz jak to posklejałem:


apc.php:
  1. <html>
  2. <head>
  3. <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
  4. <script type='text/javascript'>
  5. var oReq=new XMLHttpRequest();
  6.  
  7. function getProgress() {
  8. oReq.open('GET','test.php?progress=pasek_1',true);
  9. oReq.onreadystatechange=updateProgress
  10. oReq.send(null);
  11. }
  12.  
  13. function updateProgress() {
  14. if(oReq.readyState!=4 || oReq.status!=200) return;
  15. var progress=oReq.responseText;
  16. document.getElementById('procent').innerHTML=progress+'%';
  17. if(progress<100) setTimeout('getProgress()',500);
  18. }
  19. </script>
  20. </head>
  21. <body>
  22. <?php echo $message; ?>
  23. <div id='procent'></div>
  24. <form name='f1' method='POST' action='' enctype='multipart/form-data'
  25. onSubmit='setTimeout("getProgress()",500)' target='upload_frame'>
  26. <input type='hidden' name='APC_UPLOAD_PROGRESS' value='pasek_1' />
  27. Plik: <input type='file' name='plik' /><br>
  28. <input type='submit' value='Wyślij' />
  29. </form>
  30.  
  31. <a href=''>Odśwież</a>
  32. <iframe style='display:block' name='upload_frame'></iframe>
  33. </body>
  34. </html>


test.php:
  1. <?php
  2. if(isset($_GET['progress'])) {
  3. header('Expires: Tue, 08 Oct 1991 00:00:00 GMT');
  4. header('Cache-Control: no-cache, must-revalidate');
  5. $status=apc_fetch("upload_$_GET[progress]");
  6. if(!$status) die(0);
  7. echo round($status['current']/$status['total']*100);
  8. exit();
  9. }
  10. $message='';
  11. if($_FILES) {
  12. if($_FILES['plik']['error']==UPLOAD_ERR_OK) {
  13. $path='/tmp/';
  14. $path.=basename($_FILES['plik']['name']);
  15. if(move_uploaded_file($_FILES['plik']['tmp_name'],$path))
  16. $message='Plik przesłany poprawnie.';
  17. else
  18. $message='Błąd podczas kopiowania pliku';
  19. } else $message="Błąd podczas przesyłania pliku: ".$_FILES['plik']['error'];
  20. die($message);
  21. }
  22. ?>
JohnnyB
to wszystko (cały kod) powinno być w jednym pliku o nazwie test.php, przy czym kod PHP powinien być pierwszy. Czyli wstaw plik, który oznaczyłeś apc.php na końcu do test.php.
yarek12
teraz plik się wysyła, czyli jest ok, ale wyświetla się tylko % bez żadnej wartośći
JohnnyB
podobno czasami funkcje apc_fetch lepiej działa jeśli parametr apc.rfc1867_freq na jakąś wartość, np. apc.rfc1867_freq = 10k (po zmianie pamiętaj o przeładowaniu Apache). Jeśli to nie pomoże, to niestety, nie mam więcej pomysłów.
yarek12
Powiedz mi a do jakiego folderu jest uploadowany plik, bo ja nic nie zmieniałem kodzie, może to ma znaczenie.
JohnnyB
Docelowo pliki kopiowane są tutaj:
  1. $path='/tmp/';

ale to raczej nie powinno mieć wpływu, bo odbywa się już po zakończeniu przesyłania pliku. W trakcie uploadu pliki są zapisywane do domyślnego katalogu tymczasowego serwera. Można to zmienić ustawiając odpowiednio parametr 'upload_tmp_dir' w php.ini, ale nie sądzę żeby to był problem, raczej coś z samym APC.
Wraz z rozszerzeniem ACP jest instalowany plik apc.php (u mnie był on w katalogu /usr/share/php/apc/apc.php), jeśli nie masz to poszukaj w necie i wrzuć gdzieś na serwer - po zalogowaniu zobaczysz wiele szczegółowych danych, w szczególności na zakładce 'User cache entries' powinieneś widzieć zmienną 'upload_pasek_1' wraz z informacjami o niej.
Sprawdź też jaką masz wersję APC - u mnie jest 3.1.7, może spróbuj wgrać nowszą, jeśli masz taką możliwość.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.