0

In my view, I have an image element as follows:

<img id="ImageDisplay" class="img-thumbnail" width="280" height="280" 
                         src="@Url.Action("GetFileFromHttpPostedFile", "Image", new { File = Model.File })"/>

When I debug my code, I can see that my Model.File is not null. But when I pass it to the action method, the action method receives the parameter as null. Here is my action method:

public FileContentResult GetFileFromHttpPostedFile(HttpPostedFileBase File)
{
    byte[] imageData = new byte[File.ContentLength];
    File.InputStream.Read(imageData, 0, File.ContentLength);
    return GetFileFromData(imageData, File.ContentType);
}

Am I missing something? Can we not pass an HttpPostedFileBase to a method? Help please.

Edit: I also have the following element in the view that allows the user to populate Model.File property:

<input type="file" name="File" id="File" onchange="loadFile(event)" />

loadFile(event) simply shows a preview of the picture on the view.

Edit 2: Here is the full code for the view:

@model MyProject.Models.ViewModels.ChangeProfileModel

@{
    ViewBag.Title = "Change Your Profile";
}

<script>
    $(document).ready(function () {
        $('textarea').keyup(updateCount);
        $('textarea').keydown(updateCount);

        function updateCount() {
            var cs = [500 - $(this).val().length];
            $('#characters').text(cs);
        }
    });

    var loadFile = function (event) {
        var output = document.getElementById('ImageDisplay');

        if (output != null) {
            output.src = URL.createObjectURL(event.target.files[0]);
        }
    };
</script>

<h2>Change Your Profile Info</h2>

@using (Html.BeginForm("ChangeProfile", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    if (ViewBag.ChangesSaved == true)
    {
        <div class="alert alert-success">
            Your changes have been saved successfully! 
            <a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
        </div>
    }

    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Id)

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

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

        <div class="form-group">
            <label class="control-label col-md-2">Select an Image </label>
            <div class="col-md-10">
                <input type="file" name="File" id="File" onchange="loadFile(event)" />
            </div>
        </div>

        <div class="form-group">
            <label class="control-label col-md-2"></label>
            <div class="col-md-10">
                @if (Model.File == null)
                {
                    <img id="ImageDisplay" class="img-thumbnail" width="280" height="280" 
                         src="@Url.Action("GetImageByUser", "Image", new { id = Model.Id })"/>
                }
                else
                {
                    <img id="ImageDisplay" class="img-thumbnail" width="280" height="280" 
                         src="@Url.Action("GetFileFromHttpPostedFile", "Image", new { File = Model.File })"/>
                }
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(m => m.About, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextAreaFor(m => m.About, new { @class = "form-control", @rows = "5", @maxlength = 500 })
                <span id="characters" style="color:#999;">500</span> <span style="color:#999;">characters left</span>
                @Html.ValidationMessageFor(model => model.About, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />  
                @Html.ActionLink("Cancel", "Index", "Manage", null, new { @class = "btn btn-default" })
            </div>
        </div>
    </div>
}

In my HomeController, I have the following methods:

public ActionResult ChangeProfile()
    {
        var userId = User.Identity.GetUserId();
        var loggedInUser = UserManager.FindById(userId);

        ChangeProfileModel viewModel = new ChangeProfileModel
        {
            Id = userId,
            City = loggedInUser.City,
            Country = loggedInUser.Country,
            About = loggedInUser.About
        };

        return View(viewModel);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult ChangeProfile(ChangeProfileModel viewModel)
    {
        if (!ModelState.IsValid)
        {
            return View(viewModel);
        }

        var userId = User.Identity.GetUserId();
        var loggedInUser = UserManager.FindById(userId);
        byte[] imageData = null;

        if (viewModel.File != null)
        {
            imageData = new byte[viewModel.File.ContentLength];
            viewModel.File.InputStream.Read(imageData, 0, viewModel.File.ContentLength);
        }

        _aspNetUserRepository.EditUserInfo(userId, viewModel.City, viewModel.Country, viewModel.About,
            imageData, (viewModel.File == null) ? null: viewModel.File.ContentType);

        ViewBag.ChangesSaved = true;

        return View(viewModel);
    }

Lastly, here is my last relevant action method inside ImageController:

public FileContentResult GetFileFromHttpPostedFile(HttpPostedFileBase File)
    {
        byte[] imageData = new byte[File.ContentLength];
        File.InputStream.Read(imageData, 0, File.ContentLength);
        return GetFileFromData(imageData, File.ContentType);
    }
houman_ag
  • 227
  • 3
  • 14
  • Your need an `` in order for the user to select a file and assign it and then post a form to a controller method (you cannot set the value of ` file input problematically) –  Mar 05 '17 at 22:16
  • Yes, I do have it... that's how I populate the Model.File property. I will update the question to reflect this. Thanks – houman_ag Mar 05 '17 at 23:01
  • You need to put the input inside a form (with the `enctype` attribute set) and submit the form to a `[HttpPost]` method. You cannot use a GET. Although your `onchange` attribute suggests you might be using ajax, in which case you need to use `FormData` (refer [this answer](http://stackoverflow.com/questions/29293637/how-to-append-whole-set-of-model-to-formdata-and-obtain-it-in-mvc/29293681#29293681)) –  Mar 05 '17 at 23:08
  • I am using an [HttpPost] method and I do have enctype = "multipart/form-data". My problem is when there is an error in user's entered data, so the view refreshes with the model data, i simply want the picture that the user had selected (and which is stored in modelView.File to be displayed on the view (through the user of my attribute). However, I dont know how to do this, so I created an action method that would receive my view's file (HttpPostedFileBase) and returns a FileContentResult. I had found that if I use this inside my 'src="..."' property it usually shows the picture. – houman_ag Mar 05 '17 at 23:43
  • And are you saving your file to the server? In which case when you redirect to the method that displays that image, then set the `scr` attribute to the path you saved it to. Or are you trying to display a 'preview' of the image in the same view that the file input is in? - in which case your `src="@Url.Action(...)` makes no sense since its razor code and the value of `new { File = Model.File }` is the original value of `Model.File` which is always `null` –  Mar 05 '17 at 23:52
  • It's the latter. I'm displaying a preview in the same view, but because it's a post-back by the [HttpPost] method, it already has the viewModel properties set. so Model.File is not empty. I am updating my question with the full code for the view + pertinent methods. – houman_ag Mar 06 '17 at 03:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/137304/discussion-between-stephen-muecke-and-houman-ag). –  Mar 06 '17 at 03:07

0 Answers0