Zacznijmy od kodu widoku:
<form method="post" asp-action="Index" asp-controller="Recipe" enctype="multipart/form-data" role="form">
<div class="form-group">
<label class="btn btn-lg btn-primary" for="my-file-selector">
<input id="my-file-selector" type="file" name="files" style="display: none;" multiple />
Select Files...
</label>
<input class="btn btn-lg" type="submit" value="Upload!" />
</div>
</form>
Tak więc po kolei. asp-action="Index" asp-controller="Recipe"
– oznacza tyle, że po wysłaniu formularza wywoła się akcja „Index” z kontrolera „Recipe”.
<label class="btn btn-lg btn-primary" for="my-file-selector">
<input id="my-file-selector" type="file" name="files" style="display: none;" multiple />
Select Files...
</label>
tu zastosowałem trik polegający na tym, że ukrywamy brzydki przycisk „Wybierz plik…”, zastępując go dowolną, wybraną przez nas treścią. Dodatkową ważną kwestią jest tutaj atrybut name="files"
– to właśnie taka nazwa zmiennej musi pojawić się jako parametr akcji „Index”. Kolejna rzecz – multiple. Ten atrybut dodano w HTML5 – w naszym przypadku umożliwia wybór wielu plików.
Teraz przejdźmy do Controllera:
public class RecipeController : Controller
{
private readonly IHostingEnvironment _hostingEnvironment;
public RecipeController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
// GET: /Recipe/
public IActionResult Index()
{
return View();
}
// POST: /Recipe/Index
[HttpPost]
public async Task Index(IEnumerable files)
{
var recipes = Path.Combine(_hostingEnvironment.WebRootPath, "recipes");
if (!Directory.Exists(recipes))
{
Directory.CreateDirectory(recipes);
}
foreach (var file in files)
{
var fileName = GetFileName(file);
await file.SaveAsAsync(Path.Combine(recipes, fileName));
}
return View("RecipeUploaded");
}
}
W konstruktorze użyto parametru
IHostingEnvironment
, który przechowuje m.in. ścieżkę do root folderu strony. Jak widać – wykorzystałem to do ustalenia docelowego folderu zuploadowanych plików. Jedna z metod Index
przyjmuje parametr IEnumerable<IFormFile> files
. IEnumerable
– ponieważ użyliśmy wcześniej wspomnianego atrybutu multiple
. IFormFile
– dla input type="file"
taki oto interfejs reprezentuje jeden obiekt pliku, który został wybrany w formularzu i przesłany. Implementacja metody GetFileName(file)
nie jest pokazana, jednak jej najprostsza forma może wyglądać następująco:
private static string GetFileName(IFormFile file)
{
return ContentDispositionHeaderValue
.Parse(file.ContentDisposition)
.FileName
.Trim('"');
}
Dzięki temu pliki w folderze wwwroot\recipes
będą miały taką samą nazwę jak pliki wybrane do uploadu. Czy to dobrze? Nie do końca 😉 Ale o tym kiedy indziej.
Post jest elementem serii wpisów dotyczących projektu CookBook realizowanego w ramach konkursu „Daj się poznać”.