Wysyłanie plików na serwer PHP (z paskiem postępu)

Web Design Blog

Kategoria:
Programowanie

Data publikacji:
28 października 2019

Autor:
Paweł Mansfeld

Wysyłanie plików na serwer PHP (z paskiem postępu)

W tym tutorialu wykonamy skrypt odpowiedzialny za wysyłanie wielu plików na raz za pomocą PHP na serwer. Co więcej, wykorzystując AJAX w stylu jQuery wykonamy pasek postępu (lub jak kto woli: progressbar), który znacznie poprawi atrakcyjność interakcji z naszą aplikacją internetową przy tym procesie.

Aby wizualnie wszystko było ładniejsze użyję biblioteki Bootstrap gdzie mamy możliwość stworzenia estetycznego formularza do uploadu pliku na serwer oraz przyjemnego dla oka i animowane paska postępu.

HTML5 – formularz HTML do wysyłania plików

W części HTML należy dodać formularz do wysłania pliku. Aby można było dodać więcej niż jeden plik należy dodać artybut multiple:

<form action="upload.php" method="POST" enctype="multipart/form-data">
         <input type="submit" value="Wyślij pliki"/>
            <input type="file" class="custom-file-input"  name="image[]" multiple="">
</form>

Gdzieś pod formularzem dodajmy sobie pasek postępu ustawmy go na 0% i ukryjmy:

<div class="card-body">
   <h4 class="card-title">Pliki w trakcie wysyłania, nie zamykaj tego okna… <i class="fa fa-upload"></i></h4>
   <div class="progress m-t-20">
      <div class="progress-bar bg-success" style="width: 0%; height:15px;" role="progressbar">0%</div>
   </div>
</div>

Jeżeli zależy nam na wysłaniu tylko jednego pliku możemy po prostu usunąć atrybut multiple.

PHP – zapis plików po stronie serwera (wiele plików na raz)

W części odpowiedzialnej za PHP mamy za to taki skrypt:

<?php
      if(isset($_POST["delete"])){
        echo $_POST["delete"];
        unlink("files/".$_POST["delete"]);
    }
 if(isset($_FILES['image'])){
       $errors= array();
           $file_name = $_FILES['image']['name'];
           $file_size =$_FILES['image']['size'];
           $file_tmp =$_FILES['image']['tmp_name']; 
           $file_type=$_FILES['image']['type'];
$extensions= array("jpeg","jpg","png"); 
   foreach($file_name as $key => $value){ 
      $file_ext=strtolower(end(explode('.',$_FILES['image']['name'][$key])));
           if(in_array($file_ext,$extensions)=== false){
              $errors[]="Rozszerzenie niedozwolone.";
           } 
           if($file_size[$key] > 2097152){
              $errors[]='Plik nie może być większy niż 2 MB.';
           } 
      }  
   if(empty($errors)==true){        
      foreach($file_name as $key => $value){ 
         move_uploaded_file($file_tmp[$key],"files/".$_GET["folder"]."/".$file_name[$key]);
      echo "Success";
    } 
   }
   else{
      print_r($errors);
   }
}

Jeżeli zależy nam na wysłaniu pojedynczego pliku wystarczy usunąć foreach i [$key], który odnosi się do poszczególnych pól tablicy.

Na początku sprawdzamy czy cokolwiek zostało wprowadzone w polu input o nazwie image. Tworzymy tablicę błędów aby zbierać po drodze ewentualne błędy. Później wykorzystujemy tę samą strukturę danych ale dla dozwolonych rozszerzeń plików przesyłanych plików. Sprawdzamy czy pliki są zgodne z wymogami. Jeżeli tak to w pętli foreach używamy funkcji odpowiedzialnej za upload plików: move_uploaded_file() i tam określamy ewentualnie ścieżkę do zapisu. Jak widać u mnie zapisuje się do w folderze o niezbyt oryginalnej nazwie – files.

Oczywiście wymogi co do rozszerzenia pliku czy jego wielkości można dowolnie podmienić (wielkość w granicach ustawień serwera)

JS/jQuery/AJAX – progress bar czyli pasek postępu

Poniższy kod robi całą sprawę. Kluczowa jest tutaj zmienna xhr, która przechowuje obiekt window.XMLHttpRequest oraz eventListener „progress:

$('form').submit(function(e){
     e.preventDefault();
     var formData = new FormData($(this)[0]);
$.ajax({
      xhr: function() {
         var xhr = new window.XMLHttpRequest();
         xhr.upload.addEventListener("progress", function(evt) {
             if (evt.lengthComputable) {
                 var percentComplete = evt.loaded / evt.total;
                 var progressval = Math.round(percentComplete100)+'%';
$('.progress-bar').css('width', progressval); /* Animacja paska postępu */
$('.progress-bar').text(progressval);  /* Zmiana tekstu z procentami */
             }
        }, false);
        return xhr;
},
     url:"upload.php",
     method:"POST",
     data: formData,
     contentType:false,
     processData:false,
     enctype: 'multipart/form-data',
     success:function(output){
         /* Instrukcje wykonywane po poprawnym załadowaniu */    
     }
 });
});

Za pomocą metody CSS powiększamy szerokość paska, który na początku jest ustawiony na 0. Po zakończeniu wysyłania plików możemy wyświetlić jakiś alert lub ukryć pasek – wedle uznania.

Ten kod pozostaje niezmieniony w przypadku wysyłania jednego lub wielu plików.

Kwestie bezpieczeństwa, utrzymanie i obsługa błędów

Mam jeszcze kilka porad, które mogą się przydać w aplikacji agregującej pliki użytkowników za pomocą przesyłania (ang. upload):

Jeżeli wysyłanie plików kończy się niepowodzeniem prawdopodobnie problem powoduje nieprawidłowy chmod do folderu lub błędna ścieżka (folder do którego wysyłamy pliki musi istnieć).

Dobrze jest zabronić wysyłania plików .php i innych skryptów które można włączyć w przeglądarce. Użytkownik, który może wysyłać pliki wykonywalne a potem je uruchamiać automatycznie uzyskuje pełny dostęp do PHP serwera. To dlatego nawet w najprostszych skryptach do wysyłania plików implementuje się proste zabezpieczenie polegające na filtrowaniu plików na podstawie rozszerzenia.

Pamiętaj, że pliku przy takim wysyłaniu są nadpisywane. Aby sobie z tym poradzić, częstą praktyką jest zmiana nazwy pliku na losowy hash, pieczątkę czasu lub dopisanie do istniejącej nazwy losowego ciągu lub czasu uniksowego.

Ilość plików w jednym folderze w systemach uniksowych może być z góry ograniczona z powodu wykorzystywania danego systemu plików. Rozdzielaj równomiernie pliki do osobnych folderów. Można tutaj polegać na aktualnej dacie lub odczytywać ilość plików w bieżącym folderze za pomocą funkcji scandir();

Pamiętaj że miejsce na serwerze jest skończone. Jeżeli kończy Ci się miejsce wykorzystaj dowolną chmurę do przechowywania statycznej zawartości. Przerobienie tego skryptu na taki który wyśle pliki do chmury jest trywialne – możesz to wykonać w pętli po poprawnym zapisaniu się pliku na serwerze.

Pamiętaj aby zablokować możliwość odczytu folderu files kierując się interesami i prywatnością użytkowników. Wystarczy dodać pusty plik index.php.

Efekt końcowy

Pasek pokazałem za pomocą metody SlideDown() w tej głównej metodzie submit(). Na końcu jest odświeżenie bo zaraz obok jest lista załadowanych plików a nie chciało mi się tego robić asynchronicznie:

Podsumowanie

Pasek postępu jest bardzo ważny w przypadku wysyłania wielu plików. Informuje użytkownika ile jeszcze trzeba poczekać na zakończenie wysyłania i może zapobiec wystąpieniu różnego rodzaju błędów. To bardzo podstawowa wersja skryptu, którą można dopracowywać według własnych potrzeb i pomysłów.

Oceń artykuł na temat: Wysyłanie plików na serwer PHP (z paskiem postępu)
Średnia : 4.6 , Maksymalnie : 5 , Głosów : 7


 

Dodaj swój komentarz




Komentarze

Marek

29 października 2019

Ciekawy wpis, wszystko fajnie wytłumaczone :)

Paweł Mansfeld

29 października 2019

Dziękuję :)