Jak nahrát soubory na server pomocí prostého JavaScriptu a PHP

Psaní kódu pro nahrávání obrázků na server od začátku se zdá jako velmi skličující úkol. Vytvořím velmi jednoduchý formulář pro nahrávání, který předvede, jak data souborů fungují a jak je lze přenášet.

V tomto tutoriálu vytvoříme formulář pro nahrávání pomocí HTML , odešlete soubory pomocí JavaScriptu a zpracovat a nahrát je pomocí PHP .

Upozorňujeme, že toto není myšleno jako plně funkční, bezpečný, ověřený produkční kód. Jeho účelem je jednoduše demonstrovat jednoduchým a přímočarým způsobem, jak vytvořit svůj první formulář pro nahrávání.

  • Zobrazit zdroj na GitHubu

Předpoklady

  • Základní znalost HTML
  • Základní znalost syntaxe PHP a struktury kódu
  • Porozumění místním prostředím PHP. Pokud nevíte, co to znamená, přečtěte si prosím tento návod, jak nastavit prostředí MAMP.

Cíle

  • Vytvořte pomocí HTML nejjednodušší možnou formu pro načítání souborů z místního počítače.
  • Odešlete data z formuláře HTML do skriptu PHP s vanilkovým JavaScriptem.
  • Zpracujte data ve skriptu PHP a přesuňte místní soubory do uploads/ adresář na serveru.

Nastavení

Jak je uvedeno v předpokladech, musíte mít základní znalosti PHP a prostředí lokálních serverů.

Pokud používáte Mac, můžete vytvořit server jediným příkazem. Chcete-li to otestovat, vytvořte soubor s názvem test.php v adresáři dle vašeho výběru. Vytvořím adresář s názvem local . Úplná cesta bude Users/tania/local .

test.php
<?php echo 'This is only a test.';

V aplikaci Terminál, kterou otevřu stisknutím SPACEBAR + COMMAND a zadáním Terminal přejděte do adresáře, ve kterém jste vytvořili soubor.

cd server
php -S localhost:8888

Nyní byste měli být schopni přejít na http://localhost:8888/test.php a podívejte se na výstup kódu.

Pokud používáte Windows nebo nechcete používat příkazový řádek, nastavte MAMP.

Vytvoření formuláře pro nahrávání v HTML

V kořenovém adresáři místního serveru vytvořte index.html soubor. Vytvoříme jen rychlou kostru.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />

    <title>Upload Files</title>
  </head>

  <body>
    <!-- form goes here-->
  </body>
</html>

Do body přidáme webový formulář HTML .

<form method="post" enctype="multipart/form-data">
  <input type="file" name="files[]" multiple />
  <input type="submit" value="Upload File" name="submit" />
</form>

V tomto formuláři používáme metodu POST HTTP, kterou odesíláme data. multipart/form-data hodnota je vyžadována pro nahrávání souborů ve formulářích.

Odtud vytváříme typ vstupu souboru, který přebírá pole souborů (files[] ) a zadáváme multiple aby bylo možné vybrat více než jeden soubor. files[] může mít libovolný název – můžete použít uploads[] nebo images[] , ale nazval jsem to files[] pro jednoduchost.

Nakonec tu máme tlačítko Odeslat. Protože dalším krokem bude přidání skriptu, přidejte odkaz na soubor JavaScript, který vytvoříme.

<script src="upload.js"></script>

A to je vše, co k pohledu potřebujeme.

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />

    <title>Upload Files</title>
  </head>

  <body>
    <form method="post" enctype="multipart/form-data">
      <input type="file" name="files[]" multiple />
      <input type="submit" value="Upload File" name="submit" />
    </form>

    <script src="upload.js"></script>
  </body>
</html>

Odesílání dat formuláře prostřednictvím JavaScriptu

Právě teď kliknutím na tlačítko Odeslat ve formuláři nikam nepřejdete. Protože nemáme action která vede na adresu URL, formulář se ve výchozím nastavení odešle sám na sebe. Od index.html je soubor html, nikoli soubor PHP, na této stránce nemůže dojít ke zpracování formuláře. Místo toho odešleme formulář do PHP prostřednictvím JavaScriptu.

Vytvořte soubor s názvem upload.js .

Nejprve si definujme dvě proměnné – URL, kam chceme data odeslat, a prvek DOM pro formulář.

upload.js
// Define processing URL and form element
const url = 'process.php'
const form = document.querySelector('form')

Chystáme se přidat posluchač události, který bude sledovat odesílaný formulář, ale zabráníme spuštění výchozí akce.

// Listen for form submit
form.addEventListener('submit', (e) => {
  e.preventDefault()

  // ...
})

Pojďme shromáždit soubory s .files vlastnost a začněte nový FormData() rozhraní.

// Gather files and begin FormData
    const files = document.querySelector('[type=file]').files;
    const formData = new FormData();
});

// ...

Pro každý odeslaný soubor jej připojte k files[] pole.

// Append files to files array
for (let i = 0; i < files.length; i++) {
  let file = files[i]

  formData.append('files[]', file)
}

// ...

Nakonec použijte vestavěné rozhraní Fetch API k odeslání dat na námi zadanou adresu URL. Vytiskněte odpověď do konzole (pro testovací účely).

fetch(url, {
  method: 'POST',
  body: formData,
}).then((response) => {
  console.log(response)
})

Zde je dokončený upload.js .

upload.js
const url = 'process.php'
const form = document.querySelector('form')

form.addEventListener('submit', (e) => {
  e.preventDefault()

  const files = document.querySelector('[type=file]').files
  const formData = new FormData()

  for (let i = 0; i < files.length; i++) {
    let file = files[i]

    formData.append('files[]', file)
  }

  fetch(url, {
    method: 'POST',
    body: formData,
  }).then((response) => {
    console.log(response)
  })
})

Nyní - jak můžeme otestovat, zda všechna tato data procházejí správně? Vytiskneme data souboru.

Vytvořte nový soubor s názvem process.php a vytiskněte obsah superglobálního pole $_FILES , který bude obsahovat data pro všechny naše soubory.

process.php
<?php print_r($_FILES);

Jakmile budete mít tento soubor, pokuste se nahrát několik souborů prostřednictvím formuláře. Vytvořil jsem phplogo.png a testfile1.txt otestovat a nahrát soubor.

V Nástrojích pro vývojáře pod Konzolou , měli byste vidět odpověď takto:

Nástroje pro vývojáře -> Konzole

Response {
  type: "basic",
  url: "http://localhost:8888/process.php",
  redirected: false,
  status: 200,
  ok: true, …
}

Pokud vidíte status: 200 , to znamená, že soubor narazil na správnou adresu URL a adresa URL existuje.

Nyní v nástrojích pro vývojáře klikněte na Síť tab. Měli byste vidět název souboru process.php . Klikněte na soubor a klikněte na Odpověď . Zde byste měli vidět výstup print_r($FILES) . Bude to vypadat nějak takto:

Nástroje pro vývojáře -> Síť -> Odezva

[files] => Array
(
  [name] => Array
  (
    [0] => phplogo.png
    [1] => testfile1.txt
  )

  [type] => Array
  (
    [0] => image/png
    [1] => text/plain
  )

  [tmp_name] => Array
  (
    [0] => /private/var/xxx
    [1] => /private/var/yyy
  )

  [error] => Array
  (
    [0] => 0
    [1] => 0
  )

  [size] => Array
  (
    [0] => 16610
    [1] => 12
  )
)

Nyní víme, že prošly správné soubory spolu se všemi souvisejícími daty. Úspěch!

Zpracování dat formuláře pomocí PHP

Nyní, když shromažďujeme všechny soubory z formuláře a posíláme je na process.php s JavaScriptem, musíme přesunout data souboru pomocí PHP.

Nejprve se chceme ujistit, že se kód spustí pouze tehdy, když soubor zasáhne požadavek POST.

process.php
<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  // ...
}

Chceme se také ujistit, že soubory prošly.

if (isset($_FILES['files'])) {
  // ...
}

V kořenovém adresáři projektu vytvořte adresář s názvem uploads . Tento adresář bude muset mít 755 oprávnění přijímat příchozí soubory.

V tomto okamžiku vytvoříme pole pro chyby, nastavíme cestu k adresáři, kam mají nahrávat soubory, a nastavíme schválená rozšíření.

$errors = [];
$path = 'uploads/';
$extensions = ['jpg', 'jpeg', 'png', 'gif'];

Protože uživatel může nahrát více souborů, vytvoříme $all_files proměnnou, zjistěte počet nahrávaných souborů a vytvořte for smyčka.

$all_files = count($_FILES['files']['tmp_name']);

for ($i = 0; $i < $all_files; $i++) {
    // ...
}

Nyní pro každý soubor získáme název souboru, data dočasného souboru, typ, velikost a příponu.

$file_name = $_FILES['files']['name'][$i];
$file_tmp = $_FILES['files']['tmp_name'][$i];
$file_type = $_FILES['files']['type'][$i];
$file_size = $_FILES['files']['size'][$i];
$file_ext = strtolower(end(explode('.', $_FILES['files']['name'][$i])));

$file = $path . $file_name;

Nyní můžeme nastavit několik pravidel pro soubory. Pokud typ souboru není ve schváleném seznamu přípon nebo je soubor příliš velký, přidáme jej do pole chyb. Nastavil jsem velikost souboru na 2 megabajty.

if (!in_array($file_ext, $extensions)) {
    $errors[] = 'Extension not allowed: ' . $file_name . ' ' . $file_type;
}

if ($file_size > 2097152) {
    $errors[] = 'File size exceeds limit: ' . $file_name . ' ' . $file_type;
}

Pokud nebyly žádné chyby, můžeme pokračovat a přesunout soubor do nahrávek složku s move_uploaded_file příkaz.

if (empty($errors)) {
  move_uploaded_file($file_tmp, $file);
}

Nyní můžeme uzavřít for smyčku a vytiskněte chyby. To se nám zobrazí na kartě sítě, kterou jsme předtím použili k zobrazení výstupu $_FILES .

if ($errors) print_r($errors);

Dejte to všechno dohromady a tady je process.php .

process.php
<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_FILES['files'])) {
        $errors = [];
        $path = 'uploads/';
        $extensions = ['jpg', 'jpeg', 'png', 'gif'];

        $all_files = count($_FILES['files']['tmp_name']);

        for ($i = 0; $i < $all_files; $i++) {
            $file_name = $_FILES['files']['name'][$i];
            $file_tmp = $_FILES['files']['tmp_name'][$i];
            $file_type = $_FILES['files']['type'][$i];
            $file_size = $_FILES['files']['size'][$i];
            $file_ext = strtolower(end(explode('.', $_FILES['files']['name'][$i])));

            $file = $path . $file_name;

            if (!in_array($file_ext, $extensions)) {
                $errors[] = 'Extension not allowed: ' . $file_name . ' ' . $file_type;
            }

            if ($file_size > 2097152) {
                $errors[] = 'File size exceeds limit: ' . $file_name . ' ' . $file_type;
            }

            if (empty($errors)) {
                move_uploaded_file($file_tmp, $file);
            }
        }

        if ($errors) print_r($errors);
    }
}

Nyní to vyzkoušejte. Pokud k nahrání některých souborů použijete formulář, uvidíte je v části Nahrávání složku. Pokud se pokusíte nahrát soubor, který je příliš velký nebo nesprávného typu, zobrazí se chyby v Síti odpověď.

Závěr

Gratulujeme, úspěšně jste vytvořili funkční formulář pro nahrávání. Toto je vzrušující malý proces, pokud jste nikdy úspěšně nenahráli soubor nebo nepoužili $_FILES dříve superglobální.

Úplný zdroj je na GitHubu.

  • Zobrazit zdroj na GitHubu

Všimněte si, že se nejedná o úplný, bezpečný výrobní proces. Zde je několik věcí, které je třeba vzít v úvahu:

  • Neexistuje žádné ověřování na straně JavaScriptu. Uživateli by se měla před odesláním zobrazit chyba na frontendu, pokud je jeho soubor nesprávného typu.
  • Zacházení s více soubory se stejným názvem.
  • Tato metoda zpracování chyb je určena pouze pro proces vývoje.

Děkuji za přečtení. Mohu také vytvořit jeden o nahrávání na Amazon S3 a/nebo DigitalOcean Spaces, pokud bude zájem.