Średni · lekcja 18
Przesyłanie plików (upload) w ASP.NET Core
Wiele aplikacji webowych pozwala użytkownikowi przesłać plik — zdjęcie profilowe, załącznik do zgłoszenia, zdjęcie produktu w panelu sprzedawcy. Ta lekcja pokazuje, jak ASP.NET Core odbiera przesłane pliki, jak bezpiecznie zapisać je na serwerze, i jakich regu...
Zacznij tutaj
Po co się tego uczymy?
Wprowadzenie
Teoria
Formularz do przesyłania plików wymaga DODATKOWEGO atrybutu na znaczniku <form>: enctype="multipart/form-data" — bez niego przeglądarka wyśle TYLKO nazwę pliku jako zwykły tekst, a nie jego faktyczną zawartość. Pole pliku to zwykły <input type="file" asp-for="PlikDoWyslania" />.
Interfejs IFormFile. Przesłany plik trafia do PageModel jako właściwość typu IFormFile (oznaczona [BindProperty], dokładnie jak inne pola formularza) — udostępnia m.in. FileName (oryginalna nazwa pliku na komputerze użytkownika — NIGDY nie ufaj jej bezpośrednio, patrz niżej), Length (rozmiar w bajtach), ContentType (typ MIME, np. image/jpeg), oraz metodę CopyToAsync(strumień) zapisującą faktyczną zawartość pliku.
Bezpieczny zapis pliku — zasady, których NIGDY nie łamać:
- Nigdy nie używaj oryginalnej nazwy pliku od użytkownika jako nazwy pliku na serwerze — może zawierać znaki specjalne, ścieżki (
../../../plik— próba wyjścia poza dozwolony folder, atak Path Traversal) albo kolidować z już istniejącym plikiem. Zawsze generuj NOWĄ, bezpieczną nazwę, np.Guid.NewGuid().ToString() + Path.GetExtension(oryginalnaNazwa)(losowy unikalny identyfikator + oryginalne rozszerzenie). - Sprawdzaj rozszerzenie/typ pliku na "białej liście" dozwolonych wartości (np. tylko
.jpg,.png,.pdf) — NIGDY nie pozwalaj przesłać dowolnego typu pliku bez ograniczeń. - Ogranicz maksymalny rozmiar pliku (atrybut
[RequestSizeLimit(...)]na akcji albo globalna konfiguracja) — bez tego ktoś mógłby przesłać gigantyczny plik, przeciążając serwer albo zapełniając dysk. - Zapisuj pliki POZA folderem z kodem aplikacji (typowo w
wwwroot/uploads/dla plików mających być publicznie dostępne, albo w ogóle POZAwwwroot, jeśli plik nie powinien być bezpośrednio dostępny przez URL).
Zapis strumieniowy. using var strumien = new FileStream(pelnaSciezka, FileMode.Create); await plik.CopyToAsync(strumien); — zapisuje zawartość przesłanego pliku na dysk serwera KAWAŁEK PO KAWAŁKU (strumieniowo), zamiast wczytywać cały plik naraz do pamięci — ważne dla dużych plików.
Zobacz zależności
Schemat
Formularz z <input type="file"> (enctype="multipart/form-data")
│
│ POST z zawartością pliku
▼
PageModel.OnPostAsync()
│
▼
IFormFile Plik (właściwość [BindProperty])
│
├── sprawdź Path.GetExtension(Plik.FileName) na białej liście (.jpg, .png)
├── sprawdź Plik.Length <= maksymalny dozwolony rozmiar
│
▼
string bezpiecznaNazwa = Guid.NewGuid() + rozszerzenie
string pelnaSciezka = Path.Combine(wwwrootPath, "uploads", bezpiecznaNazwa)
│
▼
using FileStream + await Plik.CopyToAsync(strumien)
│
▼
Zapis w bazie danych: ścieżka względna do pliku (np. "uploads/abc123.jpg")
Połącz z praktyką
Przykład z życia
Formularz dodawania produktu w panelu sprzedawcy pozwala przesłać zdjęcie produktu, a formularz zgłoszenia w systemie helpdesk pozwala załączyć zrzut ekranu z opisem problemu — w obu przypadkach kluczowe jest bezpieczne zapisanie pliku pod NOWĄ, losową nazwą, żeby uniknąć konfliktów i ataków opartych na manipulacji nazwą pliku.