FileUpload: keep filenames when showing validation errors - c#

I have the following file-upload code in C# using Asp.Net MVC. The problem is that when validation errors are shown, all the files chosen by the user are lost (input boxes are cleared). Is it possible to keep the input filenames in their original ordering without using javascript? What's the simplest approach?
Controller code
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files)
{
//check for errors: if errors, ModelState.AddModelError(...);
if (!ModelState.IsValid) {
return View(files);
}
else {
//..........
}
}
View snippet
#using (Html.BeginForm("Index", "Uploader", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
<input type="file" name="files" id="file1" />
#Html.ValidationMessage("1")
</div>
<div class="form-group">
<input type="file" name="files" id="file2" />
#Html.ValidationMessage("2")
</div>
//and 2 more file input fields
<div>
<input type="submit" value="Upload Files" class="btn btn-success btn-lg" />
</div>
}

In postback you can not retain same local path which will remain in type="file".
to do this think there are two way
1) in your current code if you find any file attached then save on server and maintain some flag in hidden field and hide/show your file control with text box( having only filename)
and send it back to browser. Then on next valid submit take that file name that you have already saved. and do your process.
2) On submit of form, copy(DOM copy) all your html control(file ,textbox, hidenfield ect..) in one iframe and submit that iframe.

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.
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>
}

Related

ASP .NET MVC uploaded file is always null

I'm trying to upload a JSON file, but the MVC controller is always interpreting it as null.
View:
<h3>OR</h3><br>
#Html.TextBox("jsonFile", null, new { type = "file" })
<div class="col-md-offset-2 col-md-10 ">
<input type="submit" value="Create" class="btn btn-default submit-button" formaction="Create" />
</div>
Controller:
public ActionResult Create(HttpPostedFileBase jsonFile)
{
MessageBox.Show("Create");
String str;
if (ModelState.IsValid)
{
if (jsonFile != null)
{
MessageBox.Show("File Upload Success");
StreamReader jsonReader = new StreamReader(jsonFile.InputStream);
str = jsonReader.ReadLine();
MessageBox.Show(str);
}
else
{
MessageBox.Show("Null");
}
return RedirectToAction("Index");
}
return View(projectDetail);
}
Its the part of bigger program and I have used following code for form:
#using (Html.BeginForm( new { htmlAttributes = new { enctype = "multipart/form-data" } } ))
{
}
I get this button to upload files and file upload is also working well as I can see its uploaded status before I click Submit. Not sure why it's always null in the controller.
Have you decorated your action method with "HTTPPOST" attribute? I can't see that in your create action.
public ActionResult Create(HttpPostedFileBase jsonFile)
And also you have to update your form as blow to have controller and action method.
#using (Html.BeginForm("Create", "ControllerName", FormMethod.Post, new { id = "FormCreate", enctype = "multipart/form-data"}))

ASP.Net MVC 5 with DropZone Can't Send Additional Parameters to Controller

Ok, I am sure this already has an answer somewhere, but I can honestly tell you that everything I have tried, has failed. Here is my current configuration:
Shared _Layout.cshtml (below the footer)
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<script src="https://unpkg.com/dropzone#5/dist/min/dropzone.min.js"></script>
#await RenderSectionAsync("Scripts", required: false)
Images View
#model NewProductVM
<form id="form2" method="post" enctype="multipart/form-data" data-parsley-validate class="form-horizontal form-label-left">
<div class="form-group">
<label class="control-label col-md-2" for="first-name">
<span class="required">*</span>
</label>
<input type="hidden" value="#Model.Id" />
<div class="col-md-6" id="dropzone">
<div action="UploadFiles" class="dropzone" id="uploader">
Drop files here or click to upload.<br>
</div>
</div>
</div>
</form>
Products Controller
[HttpPost]
public async Task<IActionResult> UploadFiles(string Id)
{
bool bSuccess = true;
_ConnectionString = _Configuration.GetConnectionString("StorageConnectionString");
_ContainerName = _Configuration.GetValue<string>("StorageContainerName");
string uploads = Path.Combine(_hostingEnvironment.ContentRootPath, "Uploads");
if (!Directory.Exists(uploads))
Directory.CreateDirectory(uploads);
foreach (var iFormFile in Request.Form.Files)
{
if (iFormFile.Length > 0)
{
if (StorageHelper.IsImage(iFormFile))
{
var filePath = Path.Combine(uploads, iFormFile.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await iFormFile.CopyToAsync(stream);
}
bSuccess = await StorageHelper.UploadFileToStorage(iFormFile, filePath, _ConnectionString, _ContainerName);
System.IO.File.Delete(filePath);
ProductImage productImage = new ProductImage
{
ProductId = int.Parse(Id),
ImageURL = $"{_Configuration.GetValue<string>("StorageContainerURL")}/{iFormFile.FileName}"
};
await _imageService.AddNewImageAsync(productImage);
}
}
}
return RedirectToAction("Index");
}
I would really like to pass the entire NewProductVM model to the UploadFiles method, but there are not very many properties in the view model, so I could pass each property individually.
Right now I am taking someones suggestion to create a hidden form field and add a value I want to pass to the controller as a form field, but the parameter value in the UploadFiles method is null. I have also tried using asp-route-id="#Model.Id". When I did that, the value of id was 0 in the controller method.
Anyone have any suggestions?
Ok, I finally found the answer and I cannot believe how simple it was. I have been working on this for several hours. I found the answer here: https://social.msdn.microsoft.com/Forums/en-US/a87c6936-c0b5-4f47-b074-dbaf4c154cdd/id-parameter-returning-null-after-using-formdataappend-to-append-id-to-model-id-in-mvc-5?forum=aspmvc
This is what I did:
Images.cshtml
<form id="form2" method="post" enctype="multipart/form-data" data-parsley-validate class="form-horizontal form-label-left">
<div class="form-group">
<label class="control-label col-md-2" for="first-name">
<span class="required">*</span>
</label>
<div class="col-md-6" id="dropzone">
<div action="UploadFiles?Id=#Model.Id" class="dropzone" id="uploader">
Drop files here or click to upload.<br>
</div>
</div>
</div>
</form>
ProductsController.cs
[HttpPost]
public async Task<IActionResult> UploadFiles(int Id)
{
bool bSuccess = true;
_ConnectionString = _Configuration.GetConnectionString("StorageConnectionString");
_ContainerName = _Configuration.GetValue<string>("StorageContainerName");
//int Id = int.Parse(fc["Id"]);
string uploads = Path.Combine(_hostingEnvironment.ContentRootPath, "Uploads");
if (!Directory.Exists(uploads))
Directory.CreateDirectory(uploads);
foreach (var iFormFile in Request.Form.Files)
{
if (iFormFile.Length > 0)
{
if (StorageHelper.IsImage(iFormFile))
{
var filePath = Path.Combine(uploads, iFormFile.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await iFormFile.CopyToAsync(stream);
}
bSuccess = await StorageHelper.UploadFileToStorage(iFormFile, filePath, _ConnectionString, _ContainerName);
System.IO.File.Delete(filePath);
//ProductImage productImage = new ProductImage
//{
// ProductId = Id,
// ImageURL = $"{_Configuration.GetValue<string>("StorageContainerURL")}/{iFormFile.FileName}"
//};
//await _imageService.AddNewImageAsync(productImage);
}
}
}
return RedirectToAction("Index");
}
The part that really made it work was action="UploadFiles?Id=#Model.Id".
So basically, I am using the action to pass the values I want to my controller method.

Passing data from partial view to parent view

I am trying to pass a value from partial view to parent view. I tried the below which didn't worked for me. Can some one help me out how can I achieve this? By using the value that partial view returned, I am trying to hide a button which is in the parent view.
Parent View:
<div id="ProductCount">
#{
Html.RenderAction("ProductPartialView", "ProductList", new { area = "PR", ProductID = Model.ProductID, allowSelect = true});
}
<div class="saveBtnArea">
<input type="submit" value="Submit App" id="btn-submit" />
</div>
jQuery(document).ready(function () {
var ProductCount = jQuery("#ProductCount").val();
if (ProductCount == 0) {
jQuery("#btn-submit").hide();
}
else {
jQuery("#btn-submit").show();
}
});
Partial View:
<fieldset>
<div>
<input type="hidden" id="ProductCount" value="5" />
</div>
</fieldset>
You can implement change event for hidden input field like this
$('#ProductCount').change(function(){
var ProductCount = $(this).val();
if (ProductCount == 0) {
jQuery("#btn-submit").hide();
}
else {
jQuery("#btn-submit").show();
}
}).trigger('change');
$('#ProductCount').val(5);
$('#ProductCount').change(function(){
var ProductCount = $(this).val();
if (ProductCount == 0) {
jQuery("#btn-submit").hide();
}
else {
jQuery("#btn-submit").show();
}
}).trigger('change');
$('#ProductCount').val(5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='hidden' id='ProductCount' />
<button id='btn-submit'>Submit</button>

Reloading a page and showing a new view in the page

In my AccountController class I have this:
public ActionResult ForgotPassword(ForgotPasswordViewModel fpModel = null)
{
string method = HttpContext.Request.HttpMethod;
if (method == "GET")
{
ViewBag.Status = "CREATE_TASK";
ForgotPasswordViewModel model = this.ForgotPasswordManager.LoadForgotPasswordSettings();
if (model != null && !string.IsNullOrWhiteSpace(model.ForgotPasswordMethod) && model.ForgotPasswordMethod.Trim().ToUpper() == "TASKS")
return View(model);
}
if (method == "POST")
{
ViewBag.Status = "TASK_CREATED";
this.CreateTask(fpModel);
}
return RedirectToAction("ForgotPassword"); // Prob this is wrong?
}
Then in my View I have this:
#model Models.Account.ForgotPasswordViewModel
<div class="container" style="background-color: #f6f6f6">
#if (ViewBag.Status == "CREATE_TASK")
{
<div class="form-group">
<h4 id="SearchWelcomeHeader">Password Request</h4>
</div>
using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { role = "form", #class = "form-horizontal" }))
{
// some textboxes to fill up a password request form go in here
<button id="submit" class="btn btn-primary" style="width: 20%">Submit</button>
<button id="cancel" class="btn btn-secondary" style="width: 20%">Cancel</button>
}
}
#if (ViewBag.Status == "TASK_CREATED")
{
<p> Good Job ! You requested a new password! </p>
}
</div>
So what I am trying to accomplish is this: First they go to that page, fill up their password request form and they submit, so now it is a POST. So on a POST I go create some stuff in DB for them using that this.CreateTask(fpModel); in the code. But After then I want the page to be reloaded and show something new like "Success! We submitted your request", I will add an OK button later there too but currently something like this:
#if (ViewBag.Status == "TASK_CREATED")
{
<p> Good Job ! You requested a new password! </p>
ALSO A BUTTON, Will Add Later
}
But this doesn't work, after they Submit, it reloads the page with a "GET" request and thus showing the form again. I want them to now see the other part of the page that was success message.
RedirectToAction method returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action
So, modify return RedirectToAction("ForgotPassword"); to
return View(fpModel);

Upload file with input type file in form and c# HttpPostedFile

I try to user an input type file to upload a file but my code don't work.
the variable "filePosted" stay to null value.
My code :
HTML :
<form method="post" name="gestionmembre" runat="server" enctype="multipart/form-data">
#using (Html.BeginForm()){
<label class="lmembre" for="nom">Nom:</label>#Html.TextBox("nom")<br />
<label class="lmembre" for="prenom">Prénom:</label>#Html.TextBox("prenom", Request["Prenom"])<br />
<label class="lmembre" for="mail">Mail:</label>#Html.TextBox("mail", Request["mail"])<br />
<label class="lmembre" for="photo">Photo:</label><input id="phototelecharge" type="file" name="photo" value="Télécharger photo"/> <br />
<div class="errorform">#Html.ValidationSummary()</div>
<input id="ajoutmembre" type="submit" name="boutonmembre" value="Ajouter"/>
}
</form>
I don't know if I have to put this atributes in form tag (method runat enctype).
now, in the controler, in block to receive form values, I put :
else if (Request["boutonmembre"] == "Ajouter")
{
//Traitement de l'upload de l'image
HttpPostedFile filePosted;
filePosted = System.Web.HttpContext.Current.Request.Files["phototelecharge"];
if (filePosted != null && filePosted.ContentLength > 0)
{
string fileNameApplication = System.IO.Path.GetFileName(filePosted.FileName);
string fileExtensionApplication = System.IO.Path.GetExtension(fileNameApplication);
// generating a random guid for a new file at server for the uploaded file
string newFile = Guid.NewGuid().ToString() + fileExtensionApplication;
// getting a valid server path to save
string filePath = System.IO.Path.Combine(Server.MapPath("uploads"), newFile);
if (fileNameApplication != String.Empty)
{
filePosted.SaveAs(filePath);
}
}
}
The problem is in :
filePosted = System.Web.HttpContext.Current.Request.Files["phototelecharge"];
The variable fileposted is null.
In the webpage, I select a file fro a disk and the path of the file is realy indicate in the textbox.
Tks for help me.
David
Here is a simple example
Controller
namespace stackoverflow.Controllers
{
public class HomeController : Controller
{
public ActionResult PostFile(HttpPostedFileBase myFile)
{
System.Diagnostics.Debugger.Break();
return View();
}
}
}
View
#using (Html.BeginForm("PostFile", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div id="postdata">
<input type="file" name="myfile" id="myFile" />
<input type="submit" value="submit" />
</div>
}

Categories

Resources