How to keep input type=file field value after failed validation in ASP.NET MVC?

asp.net MvcForms

asp.net Mvc Problem Overview


I've got a simple form in an MVC app I've made. It contains a file field so users can upload an image. It all works great.

Problem is, if the form submit fails validation the content of the file field is lost (other fields remain populated, thx HtmlHelpers!). How do I keep the file field populated after a failed validation?

TIA!

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

Browsers are designed in such way because of security risks. It's impossible to set value of file input box in HTML source or by Javascript. Otherwise malicious script could steal some private file without user attention.

There is an interesting information about the subject.

Solution 2 - asp.net Mvc

As far as I know you cannot set the value of a HTML file input box. I would suggest coupling the file input box with a label or text box.

Then you can populate the it with the value from the file input box to be resubmitted later.

Solution 3 - asp.net Mvc

If the file isn't too big you could base64 it and use this as value for a hidden field.

Solution 4 - asp.net Mvc

There are flash based file uploaders. Try one of them. Some of them even falls back to regular file input box if flash and java script is not supported. I advise to look for jQuery plugins.

Solution 5 - asp.net Mvc

I would recommend doing the validation beforehand via ajax and do a partial page update. In this case, you will not lose the file.

Solution 6 - asp.net Mvc

I fail to agree with "impossible" being marked as correct answer. In case anybody is still in search of a possibility, here is the work around that worked for me. I'm using MVC5. The idea is to use a session variable. I got the idea from [ASP.Net Form][1].

My Model/ViewModel (only relevant properties):

public partial class emp_leaves
    {
        public string fileNameOrig { get; set; }
        public byte[] fileContent { get; set; }

        public HttpPostedFileBase uploadFile { get; set; }
    }

In my controller (HttpPost): //Check

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(emp_leaves emp_leaves)
{
    if (emp_leaves.uploadFile != null && emp_leaves.uploadFile.ContentLength>0 && !string.IsNullOrEmpty(emp_leaves.uploadFile.FileName))
    {
        emp_leaves.fileNameOrig = Path.GetFileName(emp_leaves.uploadFile.FileName);
        emp_leaves.fileContent = new byte[emp_leaves.uploadFile.ContentLength];
        emp_leaves.uploadFile.InputStream.Read(emp_leaves.fileContent, 0, emp_leaves.uploadFile.ContentLength);
        Session["emp_leaves.uploadFile"] = emp_leaves.uploadFile; //saving the file in session variable here
    }
    else if (Session["emp_leaves.uploadFile"] != null)
    {//if re-submitting after a failed validation you will reach here.
        emp_leaves.uploadFile = (HttpPostedFileBase)Session["emp_leaves.uploadFile"];
        if (emp_leaves.uploadFile != null && emp_leaves.uploadFile.ContentLength>0 && !string.IsNullOrEmpty(emp_leaves.uploadFile.FileName))
        {
            emp_leaves.fileNameOrig = Path.GetFileName(emp_leaves.uploadFile.FileName);
            emp_leaves.uploadFile.InputStream.Position = 0;
            emp_leaves.fileContent = new byte[emp_leaves.uploadFile.ContentLength];
            emp_leaves.uploadFile.InputStream.Read(emp_leaves.fileContent, 0, emp_leaves.uploadFile.ContentLength);    
        }
    }
//code to save follows here...
}

Finally within my edit view: here, i am conditionally showing the file upload control.

< script type = "text/javascript" >
  $("#removefile").on("click", function(e) {
    if (!confirm('Delete File?')) {
      e.preventDefault();
      return false;
    }
    $('#fileNameOrig').val('');
    //toggle visibility for concerned div
    $('#downloadlrfdiv').hide();
    $('#uploadlrfdiv').show();
    return false;
  }); <
/script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
@model PPMSWEB.Models.emp_leaves @{ HttpPostedFileBase uploadFileSession = Session["emp_leaves.uploadFile"] == null ? null : (HttpPostedFileBase)Session["emp_leaves.uploadFile"]; } @using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data"
})) { @Html.AntiForgeryToken()
<div class="row">
  @*irrelevant content removed*@
  <div id="downloadlrfdiv" @((!String.IsNullOrEmpty(Model.fileNameOrig) && (Model.uploadFile==n ull || uploadFileSession !=null)) ? "" : "style=display:none;")>
    <label>Attachment</label>
    <span>
            <strong>
                <a id="downloadlrf" href="@(uploadFileSession != null? "" : Url.Action("DownloadLRF", "emp_leaves", new { empLeaveId = Model.ID }))" class="text-primary ui-button-text-icon-primary" title="Download attached file">
                    @Model.fileNameOrig
                </a>
            </strong>
            @if (isEditable && !Model.readonlyMode)
            {
                @Html.Raw("&nbsp");
                <a id="removefile" class="btn text-danger lead">
                    <strong title="Delete File" class="glyphicon glyphicon-minus-sign">  </strong>
                </a>
            }
            </span>
  </div>
  <div id="uploadlrfdiv" @(!(!String.IsNullOrEmpty(Model.fileNameOrig) && Model.uploadFile==n ull) && !Model.readonlyMode ? "" : "style=display:none;")>
    <label>Upload File</label> @Html.TextBoxFor(model => model.uploadFile, new { @type = "file", @class = "btn btn-default", @title = "Upload file (max 300 KB)" }) @Html.ValidationMessageFor(x => x.uploadFile)
  </div>
</div>
}

[1]: https://forums.asp.net/t/1895657.aspx?ASP%20NET%20MVC%204%20Browser%20looses%20uploading%20File%20after%20Postback "asp.net forum"

Solution 7 - asp.net Mvc

You cannot set the value of a HTML file input box. As a workaround, replace the file upload box with a hidden input field when outputting the form after validation.

On submission, you populate the hidden field with the value from the file input box (to be resubmitted later). Remember to have either the file upload or hidden field name present at any one time (not both):

Note: The code below is for illustration/explanation purposes only. Replace it with the code appropriate to the language you use.

<?php /* You may need to sanitize the value of $_POST['file_upload']; 
* this is just a start */
if(isset($_POST['file_upload']) && !empty($_POST['file_upload'])){ ?>
<input type="hidden" name="file_upload" value="<?php print($_POST['file_upload']); ?>" />
<?php } else { ?>
<input type="file" name="file_upload" />
<?php } ?>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChaddeusView Question on Stackoverflow
Solution 1 - asp.net MvcAlexander ProkofyevView Answer on Stackoverflow
Solution 2 - asp.net MvcMichaelView Answer on Stackoverflow
Solution 3 - asp.net MvcPatrick CornelissenView Answer on Stackoverflow
Solution 4 - asp.net MvcgokkorView Answer on Stackoverflow
Solution 5 - asp.net MvcAGuyCalledGeraldView Answer on Stackoverflow
Solution 6 - asp.net MvcArfathView Answer on Stackoverflow
Solution 7 - asp.net MvcAgi HammerthiefView Answer on Stackoverflow