Ś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?

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ł bezpieczeństwa PRZESTRZEGAĆ, żeby ta funkcja nie stała się furtką dla ataku.

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 POZA wwwroot, 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.