
🚀 Ladda upp flera filer med HTML och PHP
En användare behöver ladda upp fem bilder, men formuläret tar bara emot en. Eller en kund ber om batchuppladdning av dokument med en titel kopplad till varje fil. Det här är ingen fantasi: det är en standarduppgift som PHP löser på trettio rader.
Problemet är att de flesta guider visar uppladdning av enstaka filer. Multiuppladdning fungerar annorlunda: en array med filer istället för en, mappning mot ytterligare fält, skydd mot överskrivning. Nedan följer en fungerande implementation från grunden till ett färdigt kodsnutt.
💡 Snabb översikt:
- Skapa ett HTML-formulär med
enctype="multipart/form-data"och ettfile-fält med attributetmultiple. - Bearbeta
$_FILES-arrayen i PHP: loopa genom index och användmove_uploaded_file()för varje fil. - Lägg till validering av filändelse och storlek innan du sparar till disk.
- För att länka "fil + titel", använd parallella arrayer
title[]ochfileUpload[].
En snabb videogenomgång av samma ämne, från HTML-formulär till att spara filer i en databas:
Steg 1: HTML-formulär för uppladdning av flera filer
Börja med att bygga formuläret. Viktiga punkter: attributet enctype satt till multipart/form-data och ett inmatningsfält med multiple. Utan enctype kommer servern helt enkelt inte att ta emot filer; detta är inget PHP-fel utan en del av HTTP-specifikationen.
Namnge fältet som en array: fileUpload[]. Hakparenteserna på slutet talar om för PHP att servern tar emot en lista, inte en enskild fil.
1 <form action="" method="post" enctype="multipart/form-data"> 2 <label>Select files:</label> 3 <input type="file" name="fileUpload[]" multiple> 4 <input type="submit" name="Submit" value="Upload"> 5 </form>
Formuläret ovan tillåter att man väljer valfritt antal filer samtidigt i fildialogen. Webbläsaren packar dem i en multipart-förfrågan och skickar dem till servern. Ingen JavaScript behövs i detta skede.
Steg 2: PHP-bearbetning av fil-arrayen
När formuläret skickas till servern fyller PHP den superglobala arrayen $_FILES. Men strukturen skiljer sig från uppladdning av enstaka filer: nycklarna name / tmp_name / size / error blir arrayer där indexet motsvarar filens sekvensnummer.
Vi loopar genom indexen och sparar varje fil med move_uploaded_file():
1 <?php 2 3 $target_dir = 'uploads/'; 4 5 if (isset($_FILES['fileUpload']['name'])) { 6 7 $total_files = count($_FILES['fileUpload']['name']); 8 9 for ($key = 0; $key < $total_files; $key++) { 10 11 // Skip empty fields (no file selected) 12 if (isset($_FILES['fileUpload']['name'][$key]) 13 && $_FILES['fileUpload']['size'][$key] > 0) { 14 15 $original_filename = $_FILES['fileUpload']['name'][$key]; 16 $target = $target_dir . basename($original_filename); 17 $tmp = $_FILES['fileUpload']['tmp_name'][$key]; 18 19 move_uploaded_file($tmp, $target); 20 } 21 } 22 }
Lägg till denna kod i ditt barntemas functions.php eller i tillägget Code Snippets, så börjar formuläret från steg 1 att ta emot filer.
Observera att basename() tar bort sökvägen från filnamnet. Detta skyddar mot path traversal-attacker: en angripare skulle kunna skicka ../../wp-config.php som namn. basename() lämnar bara kvar wp-config.php, men nästa försvarslager bör ändå vara validering av filändelse.
Steg 3: Kontroll av filtyp och storlek
Du kan inte acceptera allt webbläsaren skickar "som det är". Minimiskydd: en vitlista över tillåtna filändelser och en storleksgräns.
Filändelsekontroll med pathinfo() och flaggan PATHINFO_EXTENSION. Konvertera till gemener och jämför mot tillåtna ändelser:
1 $allowed_ext = array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'pdf', 'doc', 'docx'); 2 $ext = strtolower(pathinfo($_FILES['fileUpload']['name'][$key], PATHINFO_EXTENSION)); 3 4 if (!in_array($ext, $allowed_ext)) { 5 $errors[$key] = 'Invalid file type: ' . $ext; 6 continue; 7 }
Storlekskontroll är ännu enklare; värdet size i byte finns redan i $_FILES:
1 $max_file_size = 5 * 1024 * 1024; // 5 MB 2 3 if ($_FILES['fileUpload']['size'][$key] > $max_file_size) { 4 $errors[$key] = 'File exceeds the allowed size (5 MB)'; 5 continue; 6 }
Placera båda kontrollerna före move_uploaded_file() i samma loop. Filer som inte klarar valideringen når inte disken. Samla fel i en array och visa efter bearbetningen en lista för användaren som visar vilken fil som misslyckades och varför. Utan detta ser användarna bara ett tomt resultat och lämnar sidan i tron att formuläret är trasigt. För varje avvisad fil, rapportera namnet och orsaken, som "photo.png: ogiltig filtyp" eller "archive.zip: storleksgränsen överskriden".
Steg 4: Byta namn på filer vid uppladdning
Om två användare laddar upp photo.jpg kommer den andra filen att skriva över den första. Lösningen är att generera ett unikt namn vid sparandet. Den enklaste metoden: ersätt mellanslag med understreck och lägg till time().
1 if (isset($_FILES['fileUpload']['name'][$key]) 2 && $_FILES['fileUpload']['size'][$key] > 0) { 3 4 $original_filename = $_FILES['fileUpload']['name'][$key]; 5 6 // Extension separately 7 $ext = pathinfo($original_filename, PATHINFO_EXTENSION); 8 9 // Name without extension 10 $filename_without_ext = basename($original_filename, '.' . $ext); 11 12 // New name: no spaces + timestamp 13 $new_filename = str_replace(' ', '_', $filename_without_ext) 14 . '_' . time() . '.' . $ext; 15 16 move_uploaded_file( 17 $_FILES['fileUpload']['tmp_name'][$key], 18 $target_dir . $new_filename 19 ); 20 }
time()-stämpeln ger unikhet ner på sekundnivå. I projekt med hög trafik, ersätt den med uniqid() eller UUID: två samtidiga förfrågningar inom samma sekund räcker för att orsaka en kollision och förlora en av filerna. Dessutom avslöjar time() uppladdningsdatumet i filnamnet, vilket ibland är oönskat. En UUID från ramsey/uuid eliminerar båda problemen helt.
Steg 5: Uppladdning med extra fält för varje fil
Ibland behöver du koppla en titel, beskrivning eller kategori till varje fil. Istället för ett "titel + fil"-par skapar du flera och namnger fälten som arrayer:
1 <form action="" method="post" enctype="multipart/form-data"> 2 3 <input type="text" name="title[]" placeholder="Title"> 4 <input type="file" name="fileUpload[]"> 5 6 <input type="text" name="title[]" placeholder="Title"> 7 <input type="file" name="fileUpload[]"> 8 9 <input type="text" name="title[]" placeholder="Title"> 10 <input type="file" name="fileUpload[]"> 11 12 <input type="submit" name="Submit" value="Upload"> 13 14 </form>

På serversidan är $_POST['title'] och $_FILES['fileUpload']['name'] parallella arrayer. Index 0 i den ena motsvarar index 0 i den andra. Bearbetningen använder samma loop, men nu med titelbindning:
1 $total_files = count($_FILES['fileUpload']['name']); 2 3 for ($key = 0; $key < $total_files; $key++) { 4 5 if ($_FILES['fileUpload']['size'][$key] === 0) { 6 continue; // empty field — skip 7 } 8 9 $title = isset($_POST['title'][$key]) ? $_POST['title'][$key] : ''; 10 $tmp = $_FILES['fileUpload']['tmp_name'][$key]; 11 $fname = basename($_FILES['fileUpload']['name'][$key]); 12 $target = $target_dir . time() . '_' . $fname; 13 14 if (move_uploaded_file($tmp, $target)) { 15 // Save $title and $target to the database 16 // or into an array for further processing 17 } 18 }
Se till att antalet title[]-fält matchar antalet fileUpload[]-fält i formuläret. Om användaren lägger till fält dynamiskt via JavaScript måste alla index vara konsekutiva utan luckor. PHP kollapsar inte glesa arrayer automatiskt, så $_POST['title'][5] kan finnas medan $_FILES['fileUpload']['name'][5] inte gör det.
I praktiken täcker detta tillvägagångssätt en god hälft av kunduppgifterna: bildgallerier med bildtexter, dokumentuppladdningar med taggar, cv-inlämningar med portföljer. Samma princip med parallella arrayer fungerar för beskrivning, kategori, ordning och alla andra fält.
⁉️🤔 Vanliga frågor
Varför syns inte filen i $_FILES trots att formuläret skickades?
I nio fall av tio saknas
enctype="multipart/form-data"i<form>-taggen. Utan den skickar webbläsaren data som vanlig text, och PHP fyller inte i$_FILES. Kontrollera ocksåpost_max_sizeochupload_max_filesizeiphp.ini; om någon av dessa överskrids blir arrayen tom utan synliga fel.
Hur begränsar jag den totala uppladdningsstorleken för en enskild förfrågan?
Direktivet
post_max_sizeiphp.inikapar hela POST-förfrågans body, inklusive filer och textfält. Standardvärdet enligt PHP-dokumentationen är 8 MB. Sätt det högre änupload_max_filesizemed lite overhead för multipart-kodning (ungefär 20%). För massuppladdningar, öka båda parametrarna:upload_max_filesize = 20Mochpost_max_size = 25M.
Kan filer laddas upp asynkront via JavaScript?
Ja. Använd
FormDataochfetchmedmethod: 'POST'. Lägg tillfileUpload[]-fält medformData.append('fileUpload[]', file)för varje vald fil. Sätt inteContent-Type-headern manuellt; webbläsaren sätter automatisktmultipart/form-datamed korrekt boundary. För visuell återkoppling, använd en förloppsindikator viaXMLHttpRequest.upload.onprogresseftersomfetchinte exponerar förloppet direkt.
Hur är det med säkerheten: en angripare laddar upp ett PHP-skript istället för en bild?
Filändelsekontrollen från steg 3 är den första försvarslinjen. Andra: lagra uppladdade filer UTANFÖR webbplatsens rotmapp eller i en katalog med en
.htaccesssom inaktiverar exekvering (php_flag engine off). Tredje: byt namn på filer vid sparandet (steg 4); även omshell.phppasserar ändelsefiltret blir denshell_1718300000.php, vilket inte är farligt utan exekveringsrättigheter. Fjärde: kontrollera MIME-typen inte från$_FILES['type'](skickas av webbläsaren, lätt att förfalska), utan viafinfo(PHP:s inbyggda Fileinfo), som läser filens signatur.
Vad detta tillvägagångssätt ger: helhetsbilden
Du har byggt ett uppladdningssystem som hanterar tre scenarier direkt: batchfiler med en knapp, enstaka uppladdning med validering och "fil + metadata"-parning via parallella arrayer. Koden är ungefär trettio rader per steg, inget extra.
Om du arbetar i WordPress, placera hanteraren i ditt barntemas functions.php eller i tillägget Code Snippets. För ren PHP, lägg helt enkelt bearbetningsskriptet som formulärets action-mål.
Koden från denna artikel täcker de flesta typiska uppladdningsbehov. Återstående scenarier (delad uppladdning för stora filer, direktuppladdning till S3-kompatibel lagring och dra-och-släpp med förhandsvisning) kommer att behandlas i separata artiklar. Skriv i kommentarerna vilken som är relevant för ditt projekt, så prioriterar vi den.



