I'm trying to make uploading a file to the server at my mvc project. I wrote my class,
public class MyModule: IHttpModule
which defines the event
void app_BeginRequest (object sender, EventArgs e)
In it, I check the length of the file that the user has selected to send.
if (context.Request.ContentLength> 4096000)
{
//What should I write here, that file is not loaded? I tried
context.Response.Redirect ("address here");
//but the file is still loaded and then going on Redirect.
}
In ASP.NET MVC you don't usually write http modules to handle file uploads. You write controllers and inside those controllers you write actions. Phil Haack blogged about uploading files ni ASP.NET MVC:
You have a view containing a form:
<% using (Html.BeginForm("upload", "home", FormMethod.Post,
new { enctype = "multipart/form-data" })) { %>
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
<% } %>
And a controller action to handle the upload:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
if (file.ContentLength > 4096000)
{
return RedirectToAction("FileTooBig");
}
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
Related
I have referred to some Stack Overflow answers and managed to create an ASP.NET C# application that allows users to upload files (.txt). When I run the application, a page opens in the web browser, which shows “Choose file” and “Ok”. After I choose a file and enter “Ok”, the file is uploaded to an “uploads” folder in the project directory.
How do I edit my code to instead of the file just being uploading to the “uploads” folder, the data in the .txt file is displayed on the web browser page in JSON after I click “Ok” too?
I know that to read file the codes should be something like:
string data = File.ReadAllText(path);
return data;
However i am unsure on how to put these codes in to make the program work as required.
Here is what I have done so far:
Index.cshtml
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data"}))
{
<input type="file" name="file" />
<input type="submit" value="OK" />
}
HomeController.cs
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the filename
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
// redirect back to the index action to show the form once again
return RedirectToAction("Index");
}
}
Well, this is a bit awkward but you can do
<div>#ViewBag.JSON</div>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data"}))
{
<input type="file" name="file" />
<input type="submit" value="OK" />
}
Then in your Controller
public ActionResult Index()
{
if(TempData.ContainsKey("JSON") && !string.IsNullOrEmpty((string)TempData["JSON"]))
{
ViewBag.JSON = System.IO.File.ReadAllText((string)TempData["JSON"]);
}
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the filename
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
TempData["JSON"] = path;
file.SaveAs(path);
}
// redirect back to the index action to show the form once again
return RedirectToAction("Index");
}
Update since you don't want to return any html back, change the code like so:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the filename
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
Response.Write(System.IO.File.ReadAllText(path));
return null;
}
// redirect back to the index action to show the form once again
return RedirectToAction("Index");
}
I want to let the user select a file from his/her computer, and then upload it to Flickr. The point is that, when I upload a custom image from my computer, it all works fine, but when I add an extra field for the input file but program suddenly doesn't work.
Test.cshtml:
#using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<fieldset>
<input type="file" name="file" />
<input type="submit" value="Upload!" />
</fieldset>
}
HomeController.cs:
public ActionResult Upload(HttpPostedFileBase file, FormCollection form)
{
if (Request.QueryString["oauth_verifier"] != null && Session["RequestToken"] != null)
{
// Flickr relevant code...
var tmpFilePath = Server.MapPath("~/App_Data/Uploads/Pictures");
if (file == null || file.ContentLength == 0)
{
return RedirectToAction("Index"); // It keeps hitting this!
}
var filename = Path.GetFileName(file.FileName);
var path = Path.Combine(tmpFilePath, filename);
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
file.SaveAs(path);
string photoId = flickr.UploadPicture(path, "Test picture");
if (String.IsNullOrEmpty(photoId))
{
System.Diagnostics.Debug.WriteLine("Upload failed!");
}
System.IO.File.Delete(path);
}
else
{
// Flickr relevant code...
}
return View("Test");
}
As long as I know, because MVC is a server-side framework, first of all I need to upload the picture to my server, and then upload it to Flickr. The point is that, I want to put the file in my App_Data/Upload/Pictures folder, and then upload it to Flickr, and afterwards delete it from there. So, I want to keep my server clean.
UPDATE: It keeps hitting the return RedirectToAction("Index"); part, and redirects.
You're missing enctype="multipart/form-data" from you form tag. Without that, file data is not uploaded to the server when the form is submitted.
Change your form call to:
#using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
I am using kendo mobile to build a mobile application in which the user will be able to click and upload a photo. When they first enter the page, it will show their current photo, I want to be able to click and open file explorer on their device and have the ability to show a preview of their photo in place of the old one. Then when the click done it will send it to my MVC controller where I can then send it to where I want. I cant figure out how to send my file to the controller.
HTML
<div id="NewAccountUploadContainer">
<img id="NewAccountUpload" src="~/Images/btnCamera.png" data-bind="click: uploadPhoto" />
#using (Html.BeginForm("SendNewPhoto", "MobilePlatform", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input id="ImageUploadBtn" style="display: none" type="file" accept="image/*" />
<input type="submit" value="OK" style="display: none" />
}
<div id="ImgUploadTxt" data-bind="click: uploadPhoto">
Upload a<br />
different photo.
</div>
The #ImageUploadBtn will be triggered by the #NewAccountUpload or #ImgUploadTxt clicks in jquery which works, but I cant get it to display a file or send to my controller when I trigger the submit.
C# Controller
[HttpPost]
public ActionResult SendNewPhoto(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the fielname
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
// redirect back to the index action to show the form once again
return RedirectToAction("Index");
}
The file is always null at this point.
I'm using the Kendo for mvc4, and a mobile implementation, and I'm using the follow code, works for me:
View:
#(Html.Kendo().Upload()
.Name("files")
)
Controller
public ActionResult Submit(IEnumerable<HttpPostedFileBase> files)
{
if (files != null)
{
TempData["UploadedFiles"] = GetFileInfo(files);
}
return RedirectToAction("Result");
}
public ActionResult Result()
{
return View();
}
private IEnumerable<string> GetFileInfo(IEnumerable<HttpPostedFileBase> files)
{
return
from a in files
where a != null
select string.Format("{0} ({1} bytes)", Path.GetFileName(a.FileName), a.ContentLength);
}
I have an ASP.NET MVC Website that has a dropdown list that is being created using this in the view...
#Html.DropDownList("Programs")
Programs is populated from a Business Object collection and stuffed into the ViewBag in the index action on the Home Controller...
\\get items...
ViewBag.Programs = items;
The view also has potentially three files I am getting like this in the same view...
<input type="file" name="files" id="txtUploadPlayer" size="40" />
<input type="file" name="files" id="txtUploadCoaches" size="40" />
<input type="file" name="files" id="txtUploadVolunteers" size="40" />
All of the aforementioned controls are contained in a Form that is created in the view using...
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<!-- file and other input types -->
<input type="submit" name="btnSubmit" value="Import Data" />
}
My problems is that I cannot find a way to process all of the files AND reference the form fields.
Specifically, I need to know what Program the user selected from the dropdown.
I can process the files using this code with no problem...
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files)
//public ActionResult Index(FormCollection form)
{
_tmpFilePath = Server.MapPath("~/App_Data/uploads");
if (files == null) return RedirectToAction("Index");
foreach (var file in files)
{
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(_tmpFilePath, fileName);
if (System.IO.File.Exists(path)) System.IO.File.Delete(path);
_file = file;
file.SaveAs(path);
break; //just use the first file that was not null.
}
}
//SelectedProgramId = 0;
//DoImport();
return RedirectToAction("Index");
}
But I cannot figure how to ALSO get access to the POST form values especially the Programs dropdown selected value (and for the record there is also a checkbox that I cannot read the value from.) Fiddler shows me that the Response has the file references AND the selected program but I cannot figure out how to get them out of the POST using ASP.NET MVC.
I know this question is pretty basic but I am stilling learning the whole web/http thing not just MVC.
EDIT
Thanks for your answers. I had the thought that the answer might lie in passing in both the files and the form values into the POST.
So my last question is ... how do I change the HTML.BeginForm block to pass in both the files and form values? Right now I have ...
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
//do stuff
}
What should that using statement be to get both form values and files as separate parameters of the ActionResult?
EDIT MY EDIT
It seems that I don't have to make any changes...the debugger is showing that both files and form are non-null. Cool! Is that right?
I think that this should do it
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files, FormCollection form)
{
//handle the files
//handle the returned form values in the form collection
}
You should be able to pass in 2 parameters in the [HttpPost] action. you can also pass in the HTML name.
Edit: I also had problems with forms in ASP.net. I suggest looking into this blog post by Scott Allen.
http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
Use a ViewModel type that contains both the posted files and form values, or use the HttpRequest (accessed via the Controller.Request property) object, which has .Form[key] for POST values and .Files[key] for posted files.
I have an MVC3 application that uploads a file from the users' hard drive and manipulates it. One requirement is that the file extension should be .xls(or xlsx).
I would like to validate the file name but I would like to know what is the best option in terms of reusability and performance (and of course best coding practices).
I have the following Index view:
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<br />
<p><input type="file" id="file" name="file" size="23"/></p><br />
<p><input type="submit" value="Upload file" /></p>
}
Called by the controller action method Index:
public ActionResult Index()
{
return View("Index");
}
And the user response is handled by the Index action method (HttpPost):
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
FileServices lfileServices = new FileServices();
var lfilePath = lfileServices.UploadFile(file, "~/App_Data/uploads");
//Manipulation;
}
Now I can use the Path.GetExtension(filename) in the FileServices to get the extension and then perform the check but it will be performed just after the upload completes.
I would like to do it once the user attempts to upload the file. The only thing that came up to my mind is create a Model(or better a ViewModel) and use the remote validation from DataAnnotations.
I saw a demonstration from Scott Hanselman live but it seemed like he was not really confortable with that because the application was compiling but was not performing the check.
Has anybody a good approach in order to perform such kind of remote validation or any other solution (jQuery for instance)?
Thanks
Francesco
You could do this using javascript:
$(function () {
$('form').submit(function () {
var selectedFile = $('#file').val();
var matches = selectedFile.match(/\.(xlsx?)$/i);
if (matches == null) {
alert('please select an Excel file');
return false;
}
return true;
});
});
Of course this doesn't in any case free you from the obligation of performing the same check on the server because if the client has no javascript enabled that will be the only way. And even this wouldn't be 100% reliable as there is nothing preventing users from renaming any garbage file to .xls and upload it. Heuristics could be used on the server to try to guess the actual file type by looking at some known byte sequences.
UPDATE:
Example with remote AJAX validation (due to demand in the comments, I don't recommend it though). You could use the excellent jquery.validate plugin which by the way comes bundled with ASP.NET MVC 3:
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" id="file" name="file" size="23" data-remote-val-url="#Url.Action("CheckExtension")"/>
<br />
<input type="submit" value="Upload file" />
}
<script type="text/javascript">
$('form').validate({
rules: {
file: {
remote: $('#file').data('remote-val-url')
}
},
messages: {
file: {
remote: 'Please select an Excel file'
}
}
});
</script>
and on the server:
public ActionResult CheckExtension(string file)
{
var extension = Path.GetExtension(file ?? string.Empty);
var validExtensions = new[] { ".xls", ".xlsx" };
var isValid = validExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
return Json(isValid, JsonRequestBehavior.AllowGet);
}