Jak parsować byte[] array z modelu oraz IFormFile z modelu widoku w kontrolerze?

0

Piszę Core MVC aplikację, w której posiadam model:

public class Aktualnosci
    {
        public long ID { get; set; }
        public string Tytul { get; set; }
        public string Tresc { get; set; }
        public DateTime Dzien { get; set; }
        public byte[] AktualnosciImage { get; set; }
    }

Dla wgrywania obrazka wykorzystuję IFormFile własność w modelu widoku, który jest wołany w kontrolerze, zgodnie z dokumentacją >>TUTAJ<<:

public class AktualnosciCreateVM
    {
        public long ID { get; set; }
        [Required(ErrorMessage = "Proszę wypełnić pole.")]
        [StringLength(40, ErrorMessage = "Max 40 znaków.")]
        public string Tytul { get; set; }
        [Required(ErrorMessage = "Proszę wypełnić pole.")]
        public string Tresc { get; set; }
        [Required(ErrorMessage = "Proszę wypełnić pole.")]
        public DateTime Dzien { get; set; }
        public IFormFile AktualnosciImage { set; get; }
    }

VM jest wykorzystywany dla tworzenia i edycji wpisu. Obecnie mam problem z parsowanie public IFormFile AktualnosciImage { set; get; } oraz public byte[] AktualnosciImage { get; set; } w metodzie GET kontrolera, która zwraca VM:

[Authorize(Roles = "Moderatorzy")]
        // GET: Aktualnosci/Edit/5
        public IActionResult Edit(long? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            Aktualnosci aktualnosci = aktualnosciRepository.AktualnosciList
                .FirstOrDefault(m => m.ID == id);
            if (aktualnosci == null)
            {
                return NotFound();
            }
            else
            {
                aktualnosciCreateVM.ID = aktualnosci.ID;
                aktualnosciCreateVM.Tytul = aktualnosci.Tytul;
                aktualnosciCreateVM.Tresc = aktualnosci.Tresc;
                aktualnosciCreateVM.Dzien = aktualnosci.Dzien;
//tu mam błąd v
                aktualnosciCreateVM.AktualnosciImage = aktualnosci.AktualnosciImage.ToArray();
                return View(aktualnosciCreateVM);

            }
        }

Błąd kompilacji:

Cannot implicitly convert type 'byte[]' to
'Microsoft.AspNetCore.Http.IFormFile'

Czy jest jakiś sposób parsowania tych dwóch własności?

0

No przecież masz dokładny przykład w dokumentacji, którą wkleiłeś:

using (var memoryStream = new MemoryStream())
        {
            await model.AvatarImage.CopyToAsync(memoryStream);
            user.AvatarImage = memoryStream.ToArray();
        }
0
somekind napisał(a):

No przecież masz dokładny przykład w dokumentacji, którą wkleiłeś:

using (var memoryStream = new MemoryStream())
        {
            await model.AvatarImage.CopyToAsync(memoryStream);
            user.AvatarImage = memoryStream.ToArray();
        }

Jasne, to nie jest problemem. Moja metoda POST wygląda tak:

[Authorize(Roles = "Moderatorzy")]
        // POST: Aktualnosci/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(AktualnosciCreateVM aktualnosci)
        {
            if (ModelState.IsValid)
            {
                Aktualnosci targetAktualnosci = aktualnosciRepository.AktualnosciList
                    .FirstOrDefault(m => m.ID == aktualnosci.ID);
                using (var memoryStream = new MemoryStream())
                {
                    await aktualnosci.AktualnosciImage.CopyToAsync(memoryStream);
                    targetAktualnosci.AktualnosciImage = memoryStream.ToArray();
                    targetAktualnosci.Dzien = aktualnosci.Dzien;
                    targetAktualnosci.Tresc = aktualnosci.Tresc;
                    targetAktualnosci.Tytul = aktualnosci.Tytul;
                }
                aktualnosciRepository.SaveAktualnosci(targetAktualnosci);
                return RedirectToAction(nameof(Index));
            }
            else
            {
                return View(aktualnosci);
            }
        }

Problemem się okazało przypisanie byte[] do IFormFile w metodzie GET, ponieważ podobno jedynie użytkownik może określić IFormFile przez przeglądarkę. Więc usunąłem wiersz aktualnosciCreateVM.AktualnosciImage = aktualnosci.AktualnosciImage.ToArray();. Pojawił się niestety kolejny problem.

Dla await aktualnosci.AktualnosciImage.CopyToAsync(memoryStream); otrzymuję:

NullReferenceException: Object reference not set to an instance of an object.

Teraz to próbuję rozgryźć. Przy breakpoint na w.w. wierszu podaje mi null dla AktualnosciImage, a wszystko się wydaje zgodne z dokumentacją. Identyczny problem znalazłem w zapytaniu: https://stackoverflow.com/questions/44931714/uploading-files-with-asp-net-core-into-database-as-byte-array/50321839

1

Pokaż kod swojego widoku, który wysyła dane do tej akcji. Może nie masz enctype w <form>?

0
Ktos napisał(a):

Pokaż kod swojego widoku, który wysyła dane do tej akcji. Może nie masz enctype w <form>?

@model AktualnosciCreateVM
@{
    ViewBag.Title = "Edycja";
}
@using (Html.BeginForm("Edit", "Aktualnosci", null, FormMethod.Post, true, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    <br />
    <div class="col-md-6 kontenerStrony">
        <div class="tytulStrony col-md-12">Edycja</div>
        <div class="space"></div>
        <form asp-action="Edit" method="post">
            <input type="hidden" asp-for="ID" />
            <div class="form-group">
                <div class="col-md-10">
                    <label asp-for="AktualnosciImage">Zmień obraz dla tego wpisu:</label>
                    <input asp-for="AktualnosciImage" type="file" name="files" multiple />
                </div>
            </div>
            <div class="form-group">
                <label asp-for="Tytul">Tytuł wpisu</label>
                <div><span asp-validation-for="Tytul" class="text-danger"></span></div>
                <input asp-for="Tytul" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Tresc">Treść wpisu</label>
                <div><span asp-validation-for="Tresc" class="text-danger"></span></div>
                <textarea asp-for="Tresc" class="form-control wysiwyg"></textarea>
            </div>
            <div class="form-group">
                <label asp-for="Dzien" class="control-label">Data wpisu</label>
                <div><span asp-validation-for="Dzien" class="text-danger"></span></div>
                <input type="date" asp-for="Dzien" class="form-control" />
            </div>
            <div class="text-center">
                <button class="btn btn-success" type="submit">Zapisz</button>
                <a href="javascript:history.go(-1)" class="btn btn-primary">Powrót</a>
            </div>

            <div class="space"></div>
        </form>
    </div>
}
0

Jeżeli dobrze widzę, to masz:

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

A poniżej masz:

<form asp-action="Edit" method="post">

Czyli masz znacznik <form> wewnątrz znacznika <form>, z czego zewnętrzny ma ustawiony atrybut enctype, a wewnętrzny (a to on jest wysyłany) - nie. A bez tego atrybutu wysyłanie plików nie działa. Zdecyduj się na jedno z nich :-)

0
Ktos napisał(a):

Jeżeli dobrze widzę, to masz:

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

A poniżej masz:

<form asp-action="Edit" method="post">

Czyli masz znacznik <form> wewnątrz znacznika <form>, z czego zewnętrzny ma ustawiony atrybut enctype, a wewnętrzny (a to on jest wysyłany) - nie. A bez tego atrybutu wysyłanie plików nie działa. Zdecyduj się na jedno z nich :-)

Faktycznie, wczoraj kombinowałem żeby przekształcić z ASP.NET MVC5 i zostawiłem @using...
Teraz mam już tylko:

@model AktualnosciCreateVM
@{
    ViewBag.Title = "Edycja";
}
@Html.AntiForgeryToken()
<br />
<div class="col-md-6 kontenerStrony">
    <div class="tytulStrony col-md-12">Edycja</div>
    <div class="space"></div>
    <form asp-action="Edit" method="post" enctype="multipart/form-data">
        <input type="hidden" asp-for="ID" />
        <div class="form-group">
            <div class="col-md-10">
                <label asp-for="AktualnosciImage">Zmień obraz dla tego wpisu:</label>
                <input asp-for="AktualnosciImage" type="file" name="files" multiple />
            </div>
        </div>
(...)
<div class="text-center">
            <button class="btn btn-success" type="submit">Zapisz</button>
            <a href="javascript:history.go(-1)" class="btn btn-primary">Powrót</a>
        </div>

        <div class="space"></div>
    </form>

Ten sam błąd.
Dla AktualnosciCreateVM AktualnosciImage jest wciąż null.

2

To jeszcze zmień <input asp-for="AktualnosciImage" type="file" name="files" multiple /> i usuń z niego atrybut name, bo sobie nadpisujesz name tworzone przez asp-for i biedak nie wie, do którego pola we ViewModelu należy przypisać.

Po prostu:

<input asp-for="AktualnosciImage" type="file" multiple />

1 użytkowników online, w tym zalogowanych: 0, gości: 1