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.
Related
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)
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'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);
I wrote some code in MVC a year ago and my knowledge of the framework seems to have vanished. In the code block below, I list all the files in a directory and provide a link to download them (for authenticated users). What I want to do is give the option to delete each file as well. I just added a delete button, but I am not sure where to go from there?
#{IEnumerable<string> enumerateFiles = Directory.EnumerateFiles(Server.MapPath("~/Content/Documents"));}
#{
if (Request.IsAuthenticated)
{
<h3>Authenticated User: #User.Identity.Name</h3>
<h4>-Downloadable Files-</h4>
<ul>
#foreach (var fullPath in enumerateFiles)
{
var fileName = Path.GetFileName(fullPath);
<li> #fileName
<button type="button" id="fileRemover" value="Delete" onclick="return confirm('Are you sure?')" >Delete</button>
</li>
}
</ul>
}
else
{
<h3>Non-Authenticate User, register and/or login to see documents</h3>
}
}
The code for viewing files and deleting files should be contained within the controller. Your view is meant for simply displaying information (usually from your model) back to the user.
If I were you, I would structure my controller like this:
public class FilesController : Controller
{
public ActionResult List()
{
List<FileInfo> model = new List<FileInfo>();
// Grab all file names from the directory and place them within the model.
return View(model);
}
public ActionResult View(string fileName)
{
// Add header for content type
// Grab (and verify) file based on input parameter fileName
return File(...);
}
public ActionResult Delete(string fileName)
{
// Verify file exists
// Delete file if it exists
return RedirectToAction("List");
}
}
The filename should come as a HTTP POST variable.
So you must create an hidden field to hold the file name so that you can access the value on the action when the form is submitted.
Above the action name you would use the [HttpPost] attribute so that form submit lands on this action.
It is safe to have a HTTP POST instead of HTTP GET otherwise anyone with a url will be able to delete a file.
If you have multiple file names, then each hidden field can have the name as filename_1, filename_2 etc.
I have given you the direction where to look & investigate.
I am trying to create an application that will display images that are stored locally on the webserver. Here is what I have in my view, note that "entry" are absolute addresses like "C:\Images\Image1.jpg". However, when I run it, I get "Not allowed to load local resource: file:///C:/Images/ImageName.jpg" in the console log. So maybe it tries to access the image on the client. How do I tell my view to access the local webserver path and not look for the image source on the client? Please note that moving the images into project directory is not an option, because the images are stored on a different drive on the webserver.
<!-- language: c# -->
#model List<String>
<div style="height: 500px; overflow:scroll;">
<h2>
ScreenShots for testMachine</h2>
#foreach (var entry in Model)
{
<div class="nailthumb-container square-thumb">
<img alt="screenshot" src="#Url.Content(entry)" />
</div>
}
</div>
You cannot directly serve images outside of your ASP.NET MVC 3 application to the client. That would be a huge security vulnerability if the client could access arbitrary files on your server.
You will need to write a controller action that will return them and then point your src property of your <img> tags to this controller action.
public class ImagesController: Controller
{
public ActionResult SomeImage()
{
return File(#"C:\Images\foo.jpg", "image/jpeg");
}
}
and inside your view:
<img src="#Url.Action("SomeImage", "Images")" alt="" />
You could also pass the image name as parameter to the controller action:
public class ImagesController: Controller
{
public ActionResult SomeImage(string imageName)
{
var root = #"C:\Images\";
var path = Path.Combine(root, imageName);
path = Path.GetFullPath(path);
if (!path.StartsWith(root))
{
// Ensure that we are serving file only inside the root folder
// and block requests outside like "../web.config"
throw new HttpException(403, "Forbidden");
}
return File(path, "image/jpeg");
}
}
and in your view:
<img src="#Url.Action("SomeImage", "Images", new { image = "foo.jpg" })" alt="" />
The above code was useful for me, with a change like this
System.Web.UI.Page page = new System.Web.UI.Page();
string filePath = page.Server.MapPath("~/Log/" + fileName);
if (!filePath.StartsWith(filePath))
{
throw new HttpException(403, "Forbidden");
}
return File(filePath, "Content-Disposition", "attachment;filename=TableImportLog.csv");
}
the file thrown to the user is with file name like this "attachment;filename=TableImportLog.csv", but i want the file name as "TableErrorLog.csv"
need help for the same!