Upload File in MVC and create a link to Download the the file - asp.net-mvc

I have created this code for upload file. But it does not upload any file in the App_Data/Uploads folder i have created.
Here is the code>>
In view>>
<form action="~/Views/Home/_SaveUpdate" method="post" enctype="multipart/form-data">
<label for="file1">Filename:</label>
<input type="file" name="files" id="file1" />
<label for="file2">Filename:</label>
<input type="file" name="files" id="file2" />
<input type="submit" />
</form>
And this my Handler>>
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files)
{
foreach (var file in files)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(HttpContext.Server.MapPath("~/App_Data/Uploads"), fileName);
file.SaveAs(path);
}
}
return RedirectToAction("Index");
}
Please tell me what more needs to be done. Also, how to generate the Link to download the file.

First, the action of the form now points to ~/Views/Home/_SaveUpdate while this should be /Home/Index based on your post-Action.
Second, make sure that you have created an Upload folder inside your App_Data folder.
This should take care of the upload problem.
View:
<form action="/Home/Index" method="post" enctype="multipart/form-data">
<label for="file1">Filename:</label>
<input type="file" name="files" id="file1" />
<label for="file2">Filename:</label>
<input type="file" name="files" id="file2" />
<input type="submit" />
</form>
If you want to display download links to all uploaded files, you should store the images in a different folder than App_Data. The App_Data folder is not directly accessible due to security reasons.
A good example to show the files in a directory can be found here

I took your code and made just a few changes to test it.. and it worked.
For my tests i just changed the name of the action.
Are you sure your form Action is correct?
<form action="~/Views/Home/_SaveUpdate"
because it doesn't match with the name of your handler:
public ActionResult Index(IEnumerable ....
My test:
Make sure your Upload folder exist or you'll get an exception.
The Handler:
[HttpPost]
public ActionResult FileUploadPost(IEnumerable<HttpPostedFileBase> files)
{
foreach (var file in files)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(HttpContext.Server.MapPath("~/Uploads"), fileName);
file.SaveAs(path);
}
}
return RedirectToAction("Index");
}
The View:
<form action="FileUploadPost" method="post" enctype="multipart/form-data">
<label for="file1">Filename1:</label>
<input type="file" name="files" id="file1" />
<label for="file2">Filename2:</label>
<input type="file" name="files" id="file2" />
<input type="submit" />
</form>

Related

renaming file uploaded based on its input type's attribute(id) asp.net in mvc

View:
#using (Html.BeginForm("Edit","program","",FormMethod.Post,new {enctype = "multipart/form-data"}))
{
<div class="upload">
<input type="file" name="files" id="EpExpert"/>
<input type="file" name="files" id="EpNewbie"/>
<input type="submit" name="submit" value="submit" id="submit"/>
</div>
}
Controller:
[HttpPost]
public ActionResult Edit(tr_program program, IEnumerable<HttpPostedFileBase> files)
{
foreach (var file in files)
{
if (file != null)
{
//string extension = Path.GetExtension(file.FileName);
string path = AppDomain.CurrentDomain.BaseDirectory + "Documents/Program-PDFs/";
string filename = Path.GetFileName(file.FileName);
file.SaveAs(Path.Combine(path, filename));
}
}
}
uploaded file name should be in file-{id}.pdf
eg: file-EpNewbie.pdf
file-EpExpert.pdf
PLEASE help!!
The id is never sent to the server. You could use the name attribute instead:
#using (Html.BeginForm("Edit", "program", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="upload">
<input type="file" name="EpExpert" />
<input type="file" name="EpNewbie" />
<input type="submit" name="submit" value="submit" id="submit"/>
</div>
}
and in your controller action:
[HttpPost]
public ActionResult Edit(tr_program program)
{
string location = Server.MapPath("~/Documents/Program-PDFs");
foreach (string name in Request.Files)
{
HttpPostedFileBase file = Request.Files[name];
string filename = string.Format("file-{0}.pdf", name);
filename = Path.Combine(location, filename);
file.SaveAs(filename);
}
...
}
Obviously since you are storing all the files in the same location (~/Documents/Program-PDFs) with the same names (file-EpExpert.pdf and file-EpNewbie.pdf) if 2 users upload different files at the same time they might get overwritten. There seems to be a problem with your design and naming convention but in my answer I illustrated how you could pass the name of the file input to the controller action which could be used to build the resulting filename. Now it's up to you to take this into consideration when building your real application.
You can take an idea from here. Firstly declare id and data-id dynamic. for example
id = '#model.Id' data-id = '#model.Id'
Before submit form, use js or jquery take id value, then post form.
$("#myForm").submit(function () {
var idValue = $(this).attr('data-id');
document.getElementById('yourHiddenValue').value = idValue;
});

asp.net mvc model binder with child collections

I'm learning ASP.NET MVC and having a problem with saving a model as below:
Orders -> OrderItems
In my Create view , I will add some items into Order and when I press submit, I want the items data bind to model.OrderItems. How can I achieve this ?
[HttpPost]
public ActionResult Create(OrderViewModel model)
{
...............
//iterate item list and add to main order
foreach (var item in model.OrderItems) <~ this is what I'm trying to do
{
}
...............
}
Thanks a lot.
If they're sequential, you can create an index on each of your inputs:
<input type="text" name="OrderItems[0].Name" value="Stuff" />
<input type="text" name="OrderItems[0].Price" value="5.00" />
...
<input type="text" name="OrderItems[1].Name" value="OtherStuff" />
<input type="text" name="OrderItems[1].Price" value="15.00" />
Or you'll need to provide an index:
<input type="hidden" name="OrderItems.Index" value="#item.ItemId" />
<input type="text" name="OrderItems[#item.ItemId].Name" value="Stuff" />
<input type="text" name="OrderItems[#item.ItemId].Price" value="5.00" />

Accepting params or raw data in controller?

I was wondering if it would be possible having a "params" argument in a controller function, or something similar which would allow me to process X amount of entries in my form.
For instance, I have a form which has X amount of "name" elements, which are auto-generated through jQuery. An example of these name elements could be the following:
<input type="text" name="studentName1"></input>
<input type="text" name="studentName2"></input>
<input type="text" name="studentName3"></input>
Now, there's a different amount of student names every time, so this makes it quite complex for me to handle the form data in my controller. I had something like the following 2 examples in mind, but of course they wouldn't work in reality.
[HttpPost]
public ActionResult PostStudentNames(params string[] studentNames)
Or:
[HttpPost]
public ActionResult PostStudentNames(string[] formValues)
Can I achieve something similar to that?
I just want to chime in with a different approach you can use for this. If it's more convenient, you can model bind directly to collections of primitive or complex types. Here's 2 examples:
index.cshtml:
#using (Html.BeginForm("ListStrings", "Home"))
{
<p>Bind a collection of strings:</p>
<input type="text" name="[0]" value="The quick" /><br />
<input type="text" name="[1]" value="brown fox" /><br />
<input type="text" name="[2]" value="jumped over" /><br />
<input type="text" name="[3]" value="the donkey" /><br />
<input type="submit" value="List" />
}
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="submit" value="List" />
}
Student.cs:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
HomeController.cs:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ListStrings(List<string> items)
{
return View(items);
}
public ActionResult ListComplexModel(List<Student> items)
{
return View(items);
}
}
ListStrings.cshtml:
#foreach (var item in Model)
{
<p>#item</p>
}
ListComplexModel.cshtml:
#foreach (var item in Model)
{
<p>#item.Id. #item.Name</p>
}
The first form simply binds a list of strings. The second, binds the form data to a List<Student>. By using this approach, you can let the default model binder do some of the tedious work for you.
Updated for comment
Yes you can do that too:
Form:
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="text" name="ClassId" value="13" /><br />
<input type="submit" value="List" />
}
Controller action:
public ActionResult ListComplexModel(List<Student> items, int ClassId)
{
// do stuff
}
Mathias,
This works perfectly well without recourse to the params object. your form controls:
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="professorName" />
You would use the FormCollection object, which will contain all your form elements as either comma separated lists (if a control array) or as single properties. In the above example, this is what we'd get:
[HttpPost]
public ActionResult PostStudentNames(FormCollection formValues)
{
// basic check for rogue commas inside input controls
// would need far more sophistication in a #real# app :)
var valueStudents = formValues["studentName"].Split(',')
.Where(x => x.Length > 0).ToArray();
var valueProfessor = formValues["professorName"];
// other stuff
}
etc... At least, this is my recollection of this from a recent project. :)
<input type="text" name="studentName[0]"></input>
<input type="text" name="studentName[1]"></input>
<input type="text" name="studentName[2]"></input>
public ActionResult PostStudentNames(string[] studentName)
{
}

Pass extra parameters along with the HttpPostedFileBase object

In my MVC app, I have an upload View with GET and POST actions.
the question is how can I pass extra data to the POST Action along with the HttpPostedFileBase object, e.g., some ID for example.
You just pass it as an additional parameter
HTML:
<form action="" method="post" enctype="multipart/form-data">
<input type='text' id='txtId' name='id'/>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
Controller:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file, string id) {
if (file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}

Get binary from uploaded file (image) in ASP.NET MVC

I'm using the following code:
<form action="" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
And...
[HttpPost]
public ActionResult Index(HttpPostedFileBase file) {
if (file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
Instead of saving the file to the filesystem, I want to extract the binary data from the incoming file so I can commit the image to my database. What changes can I make to my code to support this?
Perhaps try this snippet in your solution:
byte[] imgData;
using (BinaryReader reader = new BinaryReader(file.InputStream)) {
imgData = reader.ReadBytes(file.InputStream.Length);
}
//send byte array imgData to database, or use otherwise as required.

Resources