MVC 5 Uploading file (POST) with one additional parameter - c#

I am using this simple tutorial to upload a file in my MVC5 C# VS2015 project, and without requireing additional parameters in controllers action, file gets successfuly uploaded. Here are controllers action
[HttpPost]
public string UploadFile(HttpPostedFileBase file)
{
if (file.ContentLength <= 0)
throw new Exception("Error while uploading");
string fileName = Path.GetFileName(file.FileName);
string path = Path.Combine(Server.MapPath("~/Uploaded Files"), fileName);
file.SaveAs(path);
return "Successfuly uploaded";
}
and view's form for uploading
#using (Html.BeginForm("UploadFile", "Documents", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBox("file", "", new { type = "file" })
<input type="submit" value="Dodaj fajl" />
}
In that view, I have another variable called DocumentNumber, that I need to pass on to UploadFile action. I only guess that then header of my action would look like this: public string UploadFile(HttpPostedFileBase file, int docNo) if I wanted to pass that variable on, but I also don't know how to set this value in view's form. I tried adding: new { enctype = "multipart/form-data", docNo = DocumentNumber } without success. How do I pass DocumentNumber (that needs to be hidden, not visible) from my view to controller's action with post method?

Add a parameter to your action method
[HttpPost]
public string UploadFile(HttpPostedFileBase file,int DocumentNumber)
{
}
and make sure your form has an input element with same name. It can be a hidden or visible. When you submit the form, the input value will be send with same name as of the input element name, which is matching to our action method parameter name and hence value will be mapped to that.
#using (Html.BeginForm("UploadFile", "Documents", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.TextBox("file", "", new { type = "file" })
<input type="text" name="DocumentNumber" value="123"/ >
<input type="submit" value="Dodaj fajl" />
}
If you want to use the DocumentNumber property value of your model, you may simply use one of the helper methods to generate the input element with the value (which you should set in the GET action method)
#Html.TextBoxFor(s=>s.DocumentNumber)
or for the hidden input element
#Html.HiddenFor(s=>s.DocumentNumber)

Related

Display a collection of images after each upload MVC

Have gone through the first 3 pages of Google and still can't get to the bottom of this. I have a controller which I am using to upload images:
[HttpPost]
[Authorize(Roles = "Admin,Tradesman,Customer")]
public ActionResult UploadFile(HttpPostedFileBase file)
{
// to do: ensure only valid file types are sent
try
{
if (file.ContentLength > 0)
{
using (var ctx = new ApplicationDbContext())
{
if (ModelState.IsValid)
{
// Need to check we have a current UserId and JobId before we go any furthur
var profileData = Session["UserProfile"] as UserProfileSessionData;
if (profileData.JobIdGuid.ToString().Length != 36)
{
// to do: something went horribly wrong! Redirect back to main view
}
if (profileData.UserIdGuid.ToString().Length != 36)
{
// to do: something went horribly wrong! Redirect back to main view
}
var photo = new Photos();
photo.Guid = Guid.NewGuid();
photo.Url = Server.MapPath("~/Images/2017");
photo.Extension = Path.GetExtension(file.FileName);
photo.JobGuid = profileData.JobIdGuid;
photo.UserIdGuid = profileData.UserIdGuid;
photo.Timestamp = DateTime.Now;
ctx.Photo.Add(photo);
ctx.SaveChanges();
string _path = Path.Combine(photo.Url, photo.Guid.ToString() + photo.Extension);
file.SaveAs(_path);
}
}
}
ViewBag.Message = "File Uploaded Successfully.";
return View();
}
catch
{
ViewBag.Message = "File upload failed.";
return View();
}
}
Each image is saved to a given location, the location saved to the db, happy days. Want I want though is for my images to be displayed on the same page after each upload. The model is as you'd expect just Id, Guid, Url, Extension, UserId, Timestamp.
Here is the view that uploads the images:
#{
ViewBag.Title = "UploadFile";
}
<h2>Upload File</h2>
#using (Html.BeginForm("UploadFile", "Job", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
#Html.TextBox("file", "", new { type = "file" }) 
<br />
<input type="submit" value="Next" />
#ViewBag.Message
</div>  
// to do display the images uploaded
}
Is it possible to just have some kind of for...each and have each displayed at the bottom? Anyone know how to do this! Btw this is my first C# MVC app so if this is daft question I apologise. Thanks in advance :)
You should be following the P-R-G pattern. After successfully saving the data in your HttpPost action method, you should do a redirect to your GET action method, where you will read the data you need and pass it to the view where you will display it.
I would create a view model to represent each image and use that
public class ProfileImageVm
{
public string FileName { set;get;}
public DateTime CreatedTime { set;get;}
}
Now, for your save partin your http post action method, i would advise you to not save the physical location of the file in the table. The Server.MapPath returns the physical path. Storing that is unnecessary. What if you decide to move the location to some other directory in the server tomorrow? You could simply store the unique fileName. Let's assume that you want to store all the files in the Images/2017 in app root ,you can use Server.MapPath to get the physical location so that you can store the file in disk, but do not use that to store your table record.
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
photo.Url = fileName ;
photo.Extension = Path.GetExtension(file.FileName);
With this code, it is simply storing the file name(without extension) as it is, not a unique name. That means, if you are uploading a second file with same name, it will overwrite the first one in disk. If you want to generate a unique file name, use the GetUniqueName method from this post.
Now in the GET action method, you read the Photos collection and create a list of our view model from that.
public ActionResult UploadFile()
{
var list= ctx.Photos
.Select(x=>new ProfileImageVm { FileName=x.Url + x.Extension ,
CreatedTime = x.Timestamp })
.ToList();
return View(list);
}
Now in your UploadFile view will be strongly typed to a list of ProfileImageVm, you can loop through the model data and render the images.
#model List<ProfileImageVm>
#using (Html.BeginForm("UploadFile", "Job", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.TextBox("file", "", new { type = "file" })
<input type="submit" value="Next" />
}
<h3>Images</h3>
#foreach(var item in Model)
{
<img src="~/Images/2017/#item.FileName" />
<p>Uploaded at #item.CreatedTime </p>
}
Now, after successfully saving the photo and the record in table, you will return a redirect response to the GET action.
file.SaveAs(_path);
return RedirectToAction("Upload","Job");
You can also keep the base path ~/Images/2017 in a config settings/constant and use that across your app so if you ever decide to change it to ~/Images/profilepics, there is only one place you have to change.

how to update(edit) upload files in asp.net mvc4

I want make form edit for my upload files, so I want the file delete after the new file uploaded
this for my RazorView
#model updownload.Models.updown
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(x => x.id)
<div class="container">
<div>
#Html.ValidationMessage("uploadError")
#Html.TextBoxFor(x => x.upload, new { type = "file", id = "file" })
</div>
<div class="form-group">
<label>Username:</label>
#Html.TextBoxFor(x => x.keterangan)
</div>
<div class="button">
<button>Submit</button>
</div>
</div>
}
and this for my Edit controller
[HttpPost]
public ActionResult Edit(updown viewModel, HttpPostedFileBase file)
{
var currentupdown = db.updowns.Find(viewModel.id);
if (viewModel.upload != null)
{
System.IO.File.Delete(Path.Combine(Server.MapPath("~/App_Data/upload"), viewModel.upload));
string fileName = Guid.NewGuid() + Path.GetFileName(file.FileName);
string path = Path.Combine(Server.MapPath("~/App_Data/upload"), fileName);
file.SaveAs(path);
viewModel.upload = fileName;
}
else
{
currentupdown.upload = currentupdown.upload;
}
currentupdown.keterangan = viewModel.keterangan;
db.SaveChanges();
return RedirectToAction("List", "Home");
}
I got error in this line string fileName = Guid.NewGuid() + Path.GetFileName(file.FileName);
can someone fix my code please.
sorry for my bad english
You are getting a typical null reference exception because file is NULL and you are trying to access the FileName property on that.
Why is file null ?
For file upload to work from a form, the form should have enctype attribute with value set to "multipart/form-data". Your current view code will generate the form tag without that.
<form action="/Home/Edit" method="post">
</form>
So first fix that.Also, for the file uploading to work, your file input element's name attribute value must match with the name of your HttpPostedFileBase parameter.
#using (Html.BeginForm("Edit", "Home", FormMethod.Post,
new { enctype = "multipart/form-data"}))
{
#Html.LabelFor(a => a.keterangan)
#Html.TextBoxFor(a => a.keterangan)
#Html.HiddenFor(x => x.id)
<input type="file" name="file"/>
<input value="submit" type="submit" class="btn" />
}
This will render the correct HTML markup needed to send the file.
Now in the server action method, you need to d a null check on the file parameter before trying to access that.
[HttpPost]
public ActionResult Edit(updown viewModel, HttpPostedFileBase file)
{
var currentupdown = db.updowns.Find(viewModel.id);
if (file != null)
{
var location=Server.MapPath("~/App_Data/upload");
//Delete existing file
if (!string.IsNullOrEmpty(currentupdown.upload))
{
var existingFile= Path.Combine(location, currentupdown.upload);
if (System.IO.File.Exists(existingFile))
{
System.IO.File.Delete(existingFile);
}
}
var fileName = Guid.NewGuid() + Path.GetFileName(file.FileName);
var path = Path.Combine(location, fileName);
file.SaveAs(path);
currentupdown.upload = fileName; // Update to the new file name
}
currentupdown.keterangan = viewModel.keterangan;
db.SaveChanges();
return RedirectToAction("List", "Home");
}
Since you have a view model, you can also add a new property to the viewmodel called File (of type HttpPostedFileBase) and simply use that in your server action. With this you can remove the second parameter as your view model already has a property for this. So instead of checking if(file!=null), you would be doing if(viewmodel.File!=null)
public class YourViewModel
{
public int Id { set;get;}
public string Keterangan { set;get;}
public HttpPostedFileBase File { set;get;}
}
and
[HttpPost]
public ActionResult Edit(YourViewModel viewModel)
{
// use viewModel.File as needed
}

Routing without query string

I have added the following route before the default route
routes.MapRoute(
name: "RecordDefault",
url: "{controller}/{action}/{name}",
defaults: new { controller = "Person", action = "Record" }
);
I can hit the page I want using: sitename/Person/Record/John
But I have an global search in the navigation with the following code
#using (Html.BeginForm("Record", "Person", FormMethod.Get, new { #class = "navbar-form navbar-left" }))
{
#Html.TextBox("name", "", new { #class = "form-control", placeholder = "Search Name" })
}
When I submit the form the following URL is displayed: sitename/Person/Record?name=John
What do I have to do to ensure the URL is formatted without the query string parameter?
Thanks
Not the same as the posted duplicate, that marked answer does not resolve my problem and according to the comments it also didnt work for others.
Your form generates ../Person/Record?name=John because a browser has no knowledge of your routes (which is c# code running on your server). And the HTML standards require that the value of successful form controls be added as query string values when the method is GET.
In order to generate your preferred url (../Person/Record/John), you need javascript to intercept and cancel the default submit, and build a url to navigate to. Using jQuery:
$('form').submit(function() {
var baseUrl = $(this).attr('action');
// or var baseUrl = '#Url.Action("Record", "Person")';
var url = baseUrl + '/' + $('#name').val();
location.href = url; // redirect
return false; // cancel the default submit
});
Use form post FormMethod.Post instead of Get. So the value will be not appeared in querystring.
#using (Html.BeginForm("Record", "Person", FormMethod.Post, new { #class = "navbar-form navbar-left" }))
{
#Html.TextBox("name", "", new { #class = "form-control", placeholder = "Search Name" })
}
In your Controller add the following -
[HttpPost]
public ActionResult Record(string name)
{
//code for what needs to be performed.
return View();
}
In your view add the following code replacing your existing and check -
#using (Html.BeginForm("Record", "Person", FormMethod.Post))
{
#Html.TextBox("name")
<input type="submit" />
}

How to upload image with custom name with it's original extension?

I'm trying to upload images in a form which I basically have done already, but I want the image to be named specifically for each user who registers.
For example, each email will be unique, if there is another email already in the database then the form will return false (it will not let him/her register).
So what I did was change the file name to the email he/she typed in the form.
But now the file will not have it's original extension (.jpg .png etc).
Is there a way I could pull the file original extension?
Here's what I have in my controller:
[HttpPost]
public AcitonResult Register(Registration signingUp, HttpPostedFileBase avatar)
{
var db = new AvatarDBEntities();
if (ModelState.IsValid)
{
var FindEmail = db.tblProfiles.FirstOrDefault(e => e.PROF_Email == signingUp.Email);
if (FindEmail == null)
{
var Data = db.tblProfiles.Create();
Data.PROF_Email = signingUp.Email;
if (avatar != null)
{
string profpic = System.IO.Path.GetFileName(avatar.FileName);
string profpic_name = signingUp.Email + ".jpg"; //this is what I'm trying to change
string path = System.IO.Path.Combine(Server.MapPath("~/assets/images/user_images/avatars"), profpic_name);
avatar.SaveAs(path);
}
db.tblProfiles.Add(Data);
db.SaveChanges();
}
else
{
ModelState.AddModelError("Email", "That Email already exist.");
return View();
}
}
return View();
}
View:
#using (Html.BeginForm("Register", "Main", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(r => r.Email, new { #class = "custom-input Email" })<br/>
#Html.ValidationMessageFor(a => a.Email)<br/>
<label for="avatar">Profile picture:<span class="required">*</span></label><br />
<input type="file" name="avatar" id="avatar" /><br/>
<input type="submit" />
}
The image is in the folder with the name as their email and the extension of .jpg.
I just want to pull the extension of the original file and add it after it pulls the email value.
Thanks.
What I think you're looking for is
Path.GetExtension(string fileName)
So your code becomes
string profpic_name = signingUp.Email + Path.GetExtension(avatar.FileName);
There is a method called Path.GetExtension
Store the extension in a temp variable first, then use it later.
string tempExtension = Path.GetExtension(avatar.FileName);

Routing Issue on asp.net mvc 5 GET

I am trying to get my product search URL to look like "Products/Search/{search term here}".
I am using attribute based routing and my controller action looks like this:
[HttpGet]
[Route("Products/Search/{searchTerm?}", Name="ProductSearch")]
public ActionResult Search(string searchTerm = "")
{
return View();
}
I have tried using the HTML Helper for BeginForm and BeginRouteForm (shown below) but have not had luck with either. The right action is being called, but my URL looks like "Products/Search?searchTerm"
BeginRouteForm
#using (Html.BeginRouteForm("ProductSearch", new { searchTerm = "" }, FormMethod.Get, new { Class = "navbar-form navbar-right", role = "search" }))
{
<div class="form-group">
#Html.TextBox("searchTerm", null, new { Class = "form-control", placeholder = "Item # or Name" })
</div>
<button type="submit" class="btn btn-default">Search</button>
}
BeginForm
#using (Html.BeginForm("Search", "Products", new { searchTerm = "" }, FormMethod.Get, new { Class = "navbar-form navbar-right", role = "search" }))
{
<div class="form-group">
#Html.TextBox("searchTerm", null, new { Class = "form-control", placeholder = "Item # or Name" })
</div>
<button type="submit" class="btn btn-default">Search</button>
}
I have gone through debugging and the right route is selected, the URL is just not displaying how I wanted it to. What am I missing?
Here is the solution I suggest -
You have the following controller Action -
[HttpGet]
public ActionResult Search(string searchTerm = "")
{
return View();
}
Let the view be -
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script>
$(function () {
$('#click').click(function (e) {
var name = $("#search").val();
var url = '#Url.Action("Search", "Action")' + '/' + name;
window.location.href = url;
});
});
</script>
<input type="text" name="searchText" id="search"/>
<input type="button" value="click" id="click"/>
And when you click the button -
Do not forget to have proper route to be added on to the route configuration -
routes.MapRoute(
name: "searchaction",
url: "{controller}/{action}/{searchTerm}",
defaults: new { controller = "Action", action = "Search" }
);
The problem you think you are experiencing isn't because of anything about ASP.Net MVC. All Html Forms that use the method GET will translate all input elements into QueryString parameters. This is just a W3C standard.
If you want this to work, you'll have to write jQuery to throw an event before the form is submitted, take the text value from the input store it temporarily, empty the input box, and then update the action by appending the temporary value.
I don't think that BeginRouteForm works the way that you're expecting it to. According to the documentation, all that the method does is insert a <form> using the arguments provided. If you had provided something other than an empty string for the route value such as , new { searchTerm = "somesearchterm" }, you would see that show up in the Url as "/product/search/somesearchterm". As it is now, however, the form will be processed as normal, putting the search term on the Url as a normal query parameter.

Categories

Resources