2013-12-07 15 views
12

Mam następujący model:Prześlij obraz zawarte w modelu MVC

public class Photo 
{ 
    public int PhotoId { get; set; } 
    public byte[] ImageData { get; set; } 
    public DateTime DateUploaded { get; set; } 
    public string Description { get; set; } 
    public bool IsActive { get; set; } 

} 

chciałbym użytkownika, aby być w stanie wprowadzić dane na zdjęciu, a następnie opublikować modelowi kontrolera. Moje działanie kontroler jest w następujący sposób:

[HttpPost] 
    public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo) 
    { 
     if (ModelState.IsValid) 
     { 
      photo.DateUploaded = DateTime.Now; 
      _context.Photos.Add(photo); 
      _context.SaveChanges(); 

      return RedirectToAction("Index"); 
     } 
     //we only get here if there was a problem 
     return View(photo); 
    } 

Mój pogląd jest następujący:

@using (Html.BeginForm()) 
{ 
@Html.AntiForgeryToken() 

<div class="form-horizontal"> 
    <h4>Photo</h4> 
    <hr /> 
    @Html.ValidationSummary(true) 

    <div class="form-group"> 
     @Html.LabelFor(model => model.ImageData, new { @class = "control-label col-md-2" }) 
     <div class="col-md-10"> 
      <input type="file" name="uploadImages" class="input-files" /> 
     </div> 
    </div> 

    <div class="form-group"> 
     @Html.LabelFor(model => model.DateUploaded, new { @class = "control-label col-md-2" }) 
     <div class="col-md-10"> 
      @Html.EditorFor(model => model.DateUploaded) 
      @Html.ValidationMessageFor(model => model.DateUploaded) 
     </div> 
    </div> 

    <div class="form-group"> 
     @Html.LabelFor(model => model.Description, new { @class = "control-label col-md-2" }) 
     <div class="col-md-10"> 
      @Html.EditorFor(model => model.Description) 
      @Html.ValidationMessageFor(model => model.Description) 
     </div> 
    </div> 

    <div class="form-group"> 
     @Html.LabelFor(model => model.IsActive, new { @class = "control-label col-md-2" }) 
     <div class="col-md-10"> 
      @Html.EditorFor(model => model.IsActive) 
      @Html.ValidationMessageFor(model => model.IsActive) 
     </div> 
    </div> 

    <div class="form-group"> 
     <div class="col-md-offset-2 col-md-10"> 
      <input type="submit" value="Create" class="btn btn-default" /> 
     </div> 
    </div> 
</div> 
} 

Widok wyświetlany jest ok i pozwala użytkownikowi wybrać plik z lokalnego dysku i wprowadzić inne szczegóły modelu . Mój problem polega na tym, że mimo iż model jest wysyłany do kontrolera, flagi opisu, daty i IsActive są wypełnione - dane obrazu mają wartość zerową.

Czy ktoś mógłby mi powiedzieć, co muszę zmienić, aby tablica bajtów dla zdjęcia została uwzględniona w modelu wysłanym do kontrolera?

Odpowiedz

19

Dane wejściowe pliku w widoku mają nazwę uploadImages. Nie widzę właściwości o tej nazwie w modelu widoku. Wydaje się, że masz pewną właściwość ImageData, która jest tablicą bajtów, ale w twoim widoku nie ma odpowiadającego pola wejściowego o tej nazwie.

To wyjaśnia, dlaczego otrzymujesz zero. Możesz to zrobić, szanując konwencję. Tak na przykład, jeśli masz zamiar mieć takiego pola wejściowego w widoku:

<input type="file" name="uploadImages" class="input-files" /> 

następnie upewnij się, że masz właściwości modelu widoku o tej samej nazwie. I oczywiście typu HttpPostedFileBase.

public HttpPostedFileBase UploadImages { get; set; } 

Również w widoku upewnić się, że ustawienie odpowiedniego typu zawartości multipart/form-data:

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" })) 
{ 
    ... 
} 

Prawdopodobnie chcieć przejść przez following blog post celu lepszego zapoznania się z podstawami jak przesyłanie pliki działają w środowisku ASP.NET MVC. Napisałem również similar answer here, że możesz skonsultować.

Więc po dodaniu właściwość HttpPostedFileBase z nazwą UploadImages w widoku modelu można dostosować działanie kontrolera czytać tablicę bajtów i przechowywać go aĹ ImageData:

[HttpPost] 
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo) 
{ 
    if (ModelState.IsValid) 
    { 
     photo.DateUploaded = DateTime.Now; 
     photo.ImageData = new byte[photo.UploadImages.ContentLength]; 
     photo.UploadImages.Read(photo.ImageData, 0, photo.ImageData.Length); 

     _context.Photos.Add(photo); 
     _context.SaveChanges(); 

     return RedirectToAction("Index"); 
    } 

    //we only get here if there was a problem 
    return View(photo); 
} 

Teraz pamiętać, że ten jest absolutnie okropnym rozwiązaniem. Nigdy nie rób tego w prawdziwym świecie aplikacji. W prawidłowo zaprojektowanej aplikacji będziesz miał model widoku, który będzie działał kontroler jako parametr. Nigdy nie użyjesz bezpośrednio wygenerowanego przez siebie modelu EF jako parametru do działania kontrolera. Będziesz miał model widoku z właściwością HttpPostedFileBase, który zostanie zmapowany do Twojego modelu domeny.

Tak więc w prawidłowo zaprojektowanej aplikacji będziesz mieć klasę widoku widoku modelu PhotoViewModel, którą podejmie akcja kontrolera.

+0

Kolejna wspaniała odpowiedź dzięki Darin. Stworzę dedykowany model widoku. Używałem projektu repozytorium dla tego rozwiązania, ale odkryłem, że dodatkowa warstwa abstrakcji naprawdę skomplikowała moje testy jednostkowe moq'd. –

+0

Bardzo ładna odpowiedź dzięki! +1 –

+0

nie powinno być tym: 'photo.UploadImages.InputStream.Read' –

1

Zmiana ta linia:

@using (Html.BeginForm()) 

do tego:

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" })) 

Następnie zmień:

<input type="file" name="uploadImages" class="input-files" /> 

Do:

<input type="file" name="ImageData" class="input-files" /> 

Następnie zmień tę linię:

public byte[] ImageData { get; set; } 

do tego:

public HttpPostedFileBase ImageData { get; set; } 

Wreszcie, należy użyć kodu podobnego do odczytania obraz na tablicy bajtów:

var bs = new byte[ImageData.ContentLength]; 
using (var fs = ImageData.InputStream) 
{ 
    var offset = 0; 
    do 
    { 
     offset += fs.Read(bs, offset, bs.Length - offset); 
    } while (offset < bs.Length); 
} 
1

Widok:

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" })) 
{ 
    ... 
    <input type="file" id="ImageFile" name="ImageFile" .../> 
    ... 
} 

Kontroler:

[HttpPost] 
public ActionResult Create(Photo photo, HttpPostedFileBase ImageFile) 
{ 
    byte[] buf = new byte[ImageFile.ContentLength]; 
    ImageFile.InputStream.Read(buf, 0, buf.Length); 
    photo.ImageData = buf; 
    ... 
}