Reputation: 107
Spent a lot of time going through similar articles in search for an answer to this question, here are some of them:
1Link, (Unable to show more than 2 links, but i have a bunch of them)
And most relevant 8 Link but that project is not working
But after going through this examples i still unable to sort it out. At the moment my project works without images, im able to CRUD new articles to page and remove it. But now i need to add image.
First of all, i need an image to be as part of a class (i'm creating a new article with some title, short description, text body and image) here is model i came up with, this is non working code. As i want to store link to file to db so i guess i need to create new variable ImagePath
namespace PhClub.Models
{
public class Media
{
public int Id { get; set; }
public string title { get; set; }
public string description { get; set; }
[Required, StringLength(2048), DataType(DataType.MultilineText)]
public string body { get; set; }
public string ImagePath { get; set; }
}
}
Next i need to add create form to get a filename to upload it. Here is Create view code snippet
@using (Html.BeginForm("Create", "Medias", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(model => model.title, "Title", new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.title)
@Html.ValidationMessageFor(model => model.title)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.description, "Description",new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.description)
@Html.ValidationMessageFor(model => model.description)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.body, "Media",new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.body)
@Html.ValidationMessageFor(model => model.body)
</div>
</div>
**<div class="form-group">
File name
<div class="uploadfile">
@Html.TextBoxFor(model => model.file, new { type = "file" })
@Html.ValidationMessageFor(model => model.file)
<input type="submit" value="Submit" />
</div>
</div>**
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Создать" class="btn btn-default" />
</div>
</div>
</div>
}
However here i have two uncertainties: 1. I already have a Create method in in Controller, it was generated by EF for me:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,title,description,body")] Media media)
{
if (ModelState.IsValid)
{
db.Medias.Add(media);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(media);
}
But i need to integrate it somehow with this method somehow (i took this one from one of the examples)
[HttpPost]
public ActionResult FileUpload(Media mRegister)
{
//Check server side validation using data annotation
if (ModelState.IsValid)
{
//TO:DO
var fileName = Path.GetFileName(mRegister.file.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Upload"), fileName);
mRegister.file.SaveAs(path);
ViewBag.Message = "File has been uploaded successfully";
ModelState.Clear();
}
return View();
}
I cannot have two methods with the same name - CREATE doing different things on one hand and on other I cannot give different name to it as i don't need other View for it, it needs to be shown on Create view if im understanding what is going on here. 2. Secondary - do i need to have two buttons for this? one for upload image and one for creating object?
And finally i need to show image as part of the article somehow in my Media view. At the moment it looks like that
<h2>@Model.title</h2>
<br>
<div class="media">
<p>
<strong>@Html.Raw(Model.title)</strong>
</p>
<p>
@Html.Raw(Model.description)
</p>
<p>
@Html.Raw(Model.body)
</p>
</div>
<div style ="float:right margin: 0px; 0px; 15px; 15px;">
@Html.Raw(Model.file)
</div>
From here I'm lost. I need to place that image in certain place, but don't know how.In other examples people creating bunch of other classes for file. Do i need it? What the easiest way of doing it? I know I'm not the first who came up with this question, but i could really use your help guys.
Upvotes: 1
Views: 3211
Reputation: 107
Ok, after further research i managed to make it working. This is how it looks
My class
public class Media
{
public int Id { get; set; }
public string title { get; set; }
public string description { get; set; }
[Required, StringLength(2048), DataType(DataType.MultilineText)]
public string body { get; set; }
public string ImagePath { get; set; }
}
Controller method
public ActionResult Create([Bind(Include = "Id,title,description,body")] Media media, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(HttpContext.Server.MapPath("~/Images/")
+ file.FileName);
media.ImagePath = file.FileName;
}
db.Medias.Add(media);
db.Medias.Add(media);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(media);
}
Piece of code to upload image
<fieldset>
<legend>Image</legend>
<div class="editor-label">
@Html.LabelFor(model => model.ImagePath)
</div>
<div class="editor-field">
<input id="ImagePath" title="Upload a product image"
type="file" name="file" />
</div>
</fieldset>
Media view to show final result
<div class="media">
<div style="float: right; margin: 0px 0px 15px 15px; height: 300px; width:450px">
<div id="imageContainer">
@*@Html.Raw(Model.ImagePath)*@
<img src="~/Images/@Html.Raw(Model.ImagePath)" />
</div>
</div>
<p>
<strong>@Html.Raw(Model.title)</strong>
</p>
<p>
@Html.Raw(Model.description)
</p>
<p>
@Html.Raw(Model.body)
</p>
</div>
and css formatting to resize image to div dimensions
div > img {
width: auto;
height : auto;
max-height: 100%;
max-width: 100%;
}
Link i was using for solving it is here Uploading/Displaying Images in MVC 4
Thanks everyone.
Upvotes: 0
Reputation: 218762
Your code is a little confusing to me. In your second example, you are using file
property. But your class does not have that!!!
One suggestion : Try to use PascalCasing when writing C# classes and be consistent
Ideally you should create a view model for your view
public class MediaVm
{
public string Title { get; set; }
public string Body { get; set; }
public HttpPostedFileBase Image { set; get; }
}
Now have your view strongly typed to this view model
@model MediaVm
@using (Html.BeginForm("Create", "Medias", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
@Html.ValidationSummary(false)
@Html.TextBoxFor(s=>s.Title)
@Html.TextBoxFor(s=>s.Body)
@Html.TextBoxFor(model => model.Image, new { type = "file" })
<input type="submit" />
}
Now, in your httppost action method, use the same view model as parameter, read the Image property value, save to disk with a unique name and save that unique file name in your table. We will move the code which saves the file to disk to another method which can be used from other methods as needed.
[HttpPost]
public ActionResult Create(MediaVm model)
{
if (ModelState.IsValid)
{
var fileName = SaveAndReturnFileName(model.Img);
var m = new Media { title = model.Title, body, model.Body};
m.ImagePath = fileName;
db.Medias.Add(m);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
public string SaveAndReturnFileName(HttpPostedFileBase file)
{
if (file == null)
return null;
var fileName = Path.GetFileName(file.FileName);
string randomFileName = Path.GetFileNameWithoutExtension(fileName) +
"_" +
Guid.NewGuid().ToString().Substring(0, 4)
+ Path.GetExtension(fileName);
var path = Path.Combine(Server.MapPath("~/Content/Upload/"), randomFileName);
file.SaveAs(path);
return randomFileName;
}
You should be able to access the image like the below url
yourSiteName/Content/Upload/theRandomImageNameWeSavedInTable
Upvotes: 3