My problem is with the following piece of code. I have built WebPages app where this chunk of code works perfectly, however, in my MVC5 app it copies only the local path from my PC to MSSQL database, without a GUID even. The piece of code is:
CONTROLLER:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="productId,categoryId,brandId,Name,Price,ProductImg")] Product product)
{
if (ModelState.IsValid)
{
WebImage photo = null;
var newFileName = "";
var imagePath = "";
//RIJEŠITI NESTED IF
//zašto ne prihvaća HttpPostedFileBase tip??
photo = WebImage.GetImageFromRequest();
if (photo != null)
{
newFileName = Guid.NewGuid().ToString() + "_" +
Path.GetFileName(photo.FileName);
imagePath = #"Content\Images\" + newFileName;
photo.Save(#"~\" + imagePath);
product.ProductImg = #"~\" + imagePath;
}
try
{
db.Entry(product).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
var objContext = ((IObjectContextAdapter)db).ObjectContext;
objContext.Refresh(System.Data.Entity.Core.Objects.RefreshMode.ClientWins, product);
}
return RedirectToAction("Index");
}
}
MODEL:
public class Product
{
[ScaffoldColumn(false)]
public int productId { get; set; }
[DisplayName("Category")]
public int categoryId { get; set; }
[DisplayName("Brand")]
public int brandId { get; set; }
[DisplayName("Product Name")]
[Required(ErrorMessage = "Product Name is mandatory")]
[StringLength(160)]
public string Name { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 7000.00,
ErrorMessage = "Price must be between 0.01 and 7000.00")]
public decimal Price { get; set; }
[DisplayName("Product Image")]
[StringLength(1024)]
public string ProductImg { get; set; }
public virtual Category Category { get; set; }
public virtual Brand Brand { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
Also how do I prevent this code from writing NULL to database in case that the user doesn't want to change the image?
Don't trust Filename provided by browser: some browsers send the full path others send only the file name. So you'd vetter use the following code to upload your picture/file
//-----
String path = Server.MapPath("~/content/public");
if (Request.Files != null && Request.Files.Count > 0)
{
String fileExtension = System.IO.Path.GetExtension(Request.Files[0].FileName).ToLower();
List<string> allowedExtensions = new List<string>(){".gif", ".png", ".jpeg", ".jpg" };
if (allowedExtensions.Contains(fileExtension))
{
string fileName = Guid.NewGuid().ToString();
Request.Files[0].SaveAs(path + fileName);
product.ProductImg = fileName ;
}
}
///------
and to display this image, use a simple img tag as follows
#{string imageUrl=Url.Content("~/content/public/"+Model.ProductImg); }
<img src="#imageUrl" alt=""/>
This can provide you with a guid...or you'll need to remove / or \ as follows
string fileName = Guid.NewGuid().ToString();
fileName +="_"+Request.Files[0].FileName.Split(new char[]{'/','\'}).ToList().LastOrDefault();
Related
I'm writing an article API with image and I've been following this tutorial about uploading file in ASP.NET web API, the Title and Content are saved to database as intended.
The problem here is, the images that I post are saved to my local folder but the fields Filename, Filepath, Filelength, and Filecreatedtime aren't saved to database.
Posting article to database with postman:
The ImageFolder:
GET index:
The database:
here's my Article model:
namespace Wwf_Article.Models
{
using System;
using System.Collections.Generic;
public partial class Article
{
public int ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public float FileLength { get; set; }
public DateTime FileCreatedTime { get;
}
}
and here's my POST controller
[Mime]
public async Task<IHttpActionResult> Post()
{
var fileuploadPath = HttpContext.Current.Server.MapPath("~/ImageFolder");
var multiFormDataStreamProvider = new MultiFileUploadProvider(fileuploadPath);
await Request.Content.ReadAsMultipartAsync(multiFormDataStreamProvider);
string uploadingFileName = multiFormDataStreamProvider.FileData.Select(x => x.LocalFileName).FirstOrDefault();
Article article = new Article
{
Title = HttpContext.Current.Request.Form["Title"],
Content = HttpContext.Current.Request.Form["Content"],
//these four lines below aren't saved to DB
FilePath = uploadingFileName,
FileName = Path.GetFileName(uploadingFileName),
FileLength = new FileInfo(uploadingFileName).Length,
FileCreatedTime = DateTime.Now
};
db.Articles.Add(article);
db.SaveChanges();
return Ok();
}
Any idea how to fix this problem?
Are the 4 four lines below as you mentionned part of your Article model class?
I mean (FilePath, FileName, FileLength and FileCreatedTime)
1- If yes, i think you can use a stored procedure to achieve your request like the following
CREATE PROC spInsertArticle
#Title varchar(100),
#Content varchar(100),
#FileName varchar(500),
#FilePath varchar(500),
#FileLength varchar(500)
AS
Insert Into ArticleTable(Title,Content,FileName,
FilePath,FileLength,FileContentTime)
Values (#Title,#Content,#FileName,
#FilePath,#FileLength,GetDate())
2-Go back to your api project and create a model class called ArticleModel:
public class ArticleModel
{
public string Title {get; set; }
public string Content {get; set; }
public string FileName {get; set; }
public string FilePath {get; set; }
public string FileLength {get; set; }
}
3-Create an api post method in the ArticleController
[Route("api/ArticleController/PostArticle")]
public HttpResponseMessage PostArticle(ArticleModel obj)
{
if (ModelState.IsValid)
{
try
{
string PhotoPath = Convert.ToString(ConfigurationManager.AppSettings["ImagePath"]);
ArticleModel newObj = new ArticleModel();
newObj.Title = obj.Title ;
newObj.Content = obj.Content;
newObj.FileName = obj.FileName;
newObj.FilePath = obj.FilePath;
newObjl.FileLength = obj.FileLength;
if (String.IsNullOrEmpty(newObj.Content))
{
}
else
{
string startingFilePath = PhotoPath;
string FilePath = SaveImage(newObj.Content, startingFilePath, newObj.FileName);
FileInfo fInfo = new FileInfo(FilePath);
newObj.Content = fInfo.Name;
}
ArticleEntities context = new ArticleEntities();
var newArticle = context.spInsertArticle(newObj.Title, newObj.Content,
newObj.FileName, newObj.FilePath, newObj.FileLength);
return Request.CreateResponse(HttpStatusCode.Created, newArticle);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex);
}
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, ModelState);
}
}
4-And finally create the SaveImage method mentionned in the PostArticle method
private string SaveImage(string base64, string FilePath, string ImageName)
{
//Get the file type to save in
var FilePathWithExtension = "";
string localBase64 = "";
if (base64.Contains("data:image/jpeg;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".jpg";
localBase64 = base64.Replace("data:image/jpeg;base64,", "");
}
else if (base64.Contains("data:image/png;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".png";
localBase64 = base64.Replace("data:image/png;base64,", "");
}
else if (base64.Contains("data:image/bmp;base64"))
{
FilePathWithExtension = FilePath + ImageName + ".bmp";
localBase64 = base64.Replace("data:image/bmp;base64", "");
}
else if (base64.Contains("data:application/msword;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".doc";
localBase64 = base64.Replace("data:application/msword;base64,", "");
}
else if (base64.Contains("data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".docx";
localBase64 = base64.Replace("data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,", "");
}
else if (base64.Contains("data:application/pdf;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".pdf";
localBase64 = base64.Replace("data:application/pdf;base64,", "");
}
using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(localBase64)))
{
using (FileStream fs = new FileStream(FilePathWithExtension, FileMode.Create, FileAccess.Write))
{
//Create the specified directory if it does not exist
var photofolder = System.IO.Path.GetDirectoryName(FilePathWithExtension);
if (!Directory.Exists(photofolder))
{
Directory.CreateDirectory(photofolder);
}
ms.WriteTo(fs);
fs.Close();
ms.Close();
}
}
return FilePathWithExtension;
}
5-Try this in either Postman or swagger and it will work for you. I am available for any discussion
First of all, create a Generic interface repo class
public interface IRepository<T> where T : class
{
T GetById(int id);
void Add(T entity);
}
Create a class that will implement the interface
public class EFRepository<T> : IRepository<T> where T : class
{
public EFRepository(PayrollcshrnewEntities dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected ArticleEntities DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual T GetById(int id)
{
return DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
}
For some reason after I delete the edmx and recreate the EF designer table with ADO, the POST controller works.
It turns out there's missing fields in the edmx diagram.
Thanks for all the help anyway, guys
I've created a CSV parser using the most recommended nuget I could find on here, CSVReader. I think my code is almost there its just posting the file to the controller actionmethod I can't quite find enough on. I get the error:
System.IO.FileNotFoundException: 'Could not find file 'C:\Program Files (x86)\IIS Express\System.Web.HttpPostedFileWrapper'.'
Controller action method:
[HttpPost]
public ActionResult CreateBulk(HttpPostedFileBase attachmentcsv)
{
if (ModelState.IsValid)
{
using (CsvReader csv = new CsvReader(new StreamReader(attachmentcsv.ToString()), true))
{
csv.Configuration.HasHeaderRecord = true;
var records = csv.GetRecords<Client>().ToList();
foreach (var item in records)
{
String Strip = item.homePage.Replace("https://www.", "").Replace("http://www.", "").Replace("https://", "").Replace("http://", "").Replace("www.", "");
string[] URLtests = { "https://www." + Strip, "http://www." + Strip, "https://" + Strip, "http://" + Strip };
string[] Metric = MajesticFunctions.MajesticChecker(URLtests);
var userId = User.Identity.GetUserId();
var UserTableID = db.UserTables.Where(c => c.ApplicationUserId == userId).First().ID;
var newclient = new Client { clientN = item.clientN, homePage = Metric[0], clientEmail = item.clientEmail, contName = item.contName.First().ToString().ToUpper() + item.contName.Substring(1), monthlyQuota = item.monthlyQuota, TrustFlow = Int32.Parse(Metric[1]), CitationFlow = Int32.Parse(Metric[2]), RI = Int32.Parse(Metric[3]), MJTopicsID = item.MJTopicsID, UserTableID = UserTableID };
ViewBag.newdomain = newclient;
db.Clients.Add(newclient);
db.SaveChanges();
return RedirectToAction("Index");
}
}
}
return RedirectToAction("Index");
View upload button:
#using (Html.BeginForm("CreateBulk", "Clients", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
<label for="attachment">Select a Csv File</label>
<label class="btn btn-default btn-file">
<input type="file" name="attachmentcsv" id="attachmentcsv" hidden>
</label>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
}
Client Model:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Linkofy.Models
{
public class Client
{
public int ID { get; set; }
[Required]
[Display(Name = "Client")]
public string clientN { get; set; }
[Display(Name = "Website")]
public string homePage{ get; set; }
[EmailAddress]
[Display(Name = "Contact Email")]
public string clientEmail { get; set; }
[Display(Name = "Contact Name")]
public string contName { get; set; }
[Display(Name = "Monthly")]
public int monthlyQuota { get; set; }
[Display(Name = "TF")]
public int TrustFlow { get; set; }
[Display(Name = "CF")]
public int CitationFlow { get; set; }
[Display(Name = "RIPs")]
public int RI { get; set; }
public int? MJTopicsID { get; set; }
public virtual MJTopics MJTopics { get; set; }
public int UserTableID { get; set; }
public virtual UserTable UserTable { get; set; }
public virtual ICollection<Link> Links { get; set; }
public virtual ICollection<Status> Statuss { get; set; }
}
}
You should check out: File upload in MVC
But looking into your code there are a few things that I wanted to point out:
Isolating the attachmentcsv.ToString() line it appears that this returns the type of System.Web.HttpPostedFileWrapper, this is why this string is being appended onto the file location.
I believe you may be looking for the attachmentcsv.FileName, which according to documentation of the type (https://msdn.microsoft.com/en-us/library/system.web.httppostedfilewrapper(v=vs.110).aspx)
Gets the fully qualified name of the file on the client
I'm not sure if you are using the framework or the core version of ASP, but I believe in the framework version of ASP the "correct" way of accessing upload files (as indicated in the linked answer) is to go through the Request object:
Request.Files.
https://msdn.microsoft.com/en-us/library/system.web.httprequest.files(v=vs.110).aspx
In the core version of ASP you can have a IFileForm as indicated here:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
// full path to file in temp location
var filePath = Path.GetTempFileName();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
// process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size, filePath});
}
Hope this helps
I'm using c# mvc with EF and i'm building a website. When i'm updating a table with new values it gives the following error.
{"Violation of PRIMARY KEY constraint 'PK_Table_1_1'. Cannot insert duplicate key in object 'dbo.User'. The duplicate key value is (shan#gmail.com).\r\nThe statement has been terminated."}
Here is my design of the table.
Here is my controller file
[HttpPost]
public ActionResult Manage(ManageViewModel manageviewmodel)
{
TheFoodyContext db = new TheFoodyContext();
string UserEmail = Session["UserEmail"].ToString();
User user_to_update = db.Users.Find(UserEmail);
if (ModelState.IsValid)
{
try
{
HttpPostedFileBase photo = Request.Files["photo"];
if (photo != null && photo.ContentLength > 0)
{
var path = "";
var fileName = Path.GetFileName(photo.FileName);
var extension = Path.GetExtension(photo.FileName);
var allowedExtensions = new[] {".Jpg", ".png", ".jpg", "jpeg"};
if (allowedExtensions.Contains(extension))
{
string name = Path.GetFileNameWithoutExtension(fileName);
string myfile = name + "_" + UserEmail + extension;
path= Path.Combine(Server.MapPath("~/Img"), myfile);
photo.SaveAs(path);
user_to_update.photo = myfile;
}
else
{
ViewBag.message = "Please choose only Image file";
}
user_to_update.email = UserEmail;
user_to_update.fname = manageviewmodel.FirstName;
user_to_update.lname = manageviewmodel.LastName;
user_to_update.phone = manageviewmodel.Phone;
user_to_update.address = manageviewmodel.Address;
user_to_update.city = manageviewmodel.City;
user_to_update.postcode = Convert.ToDecimal(manageviewmodel.PostCode);
user_to_update.district = manageviewmodel.District;
user_to_update.user_type = manageviewmodel.UserType;
user_to_update.status = manageviewmodel.Status;
user_to_update.photo = path;
db.Users.Add(user_to_update);
db.SaveChanges();
Session["UserEmail"] = UserEmail;
Session["FirstName"] = manageviewmodel.FirstName;
Session["LastName"] = manageviewmodel.LastName;
Session["Address"] = manageviewmodel.Address;
Session["City"] = manageviewmodel.City;
Session["PostCode"] = manageviewmodel.PostCode;
Session["District"] = manageviewmodel.District;
Session["UserType"] = manageviewmodel.UserType;
Session["Status"] = manageviewmodel.Status;
Session["Phone"] = manageviewmodel.Phone;
return RedirectToAction("Manage");
}
}
catch (Exception ex)
{
return View(ex.Message);
}
return View(manageviewmodel);
}
return View(manageviewmodel);
}
Here is my Model file
public class ManageViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string photo { get; set; }
public string Address { get; set; }
public string City { get; set; }
public int PostCode { get; set; }
public string District { get; set; }
public string UserType { get; set; }
public string Status { get; set; }
}
You don't need to add the user again (it already exists and EF tracks changes), simply call SaveChanges and you are done.
Just remove this line:
db.Users.Add(user_to_update);
and it should work (unless there are more errors).
I am running into a problem. I have my database properly configured and image is saving in database properly but dont know how to construct url for the image saved in database as i have to supply it to the knockout view model for binding.
public JsonResult GetPosts()
{
var ret = (from post in db.Posts.ToList()
orderby post.PostedDate descending
select new
{
Message = post.Message,
PostedBy = post.PostedBy,
PostedByName = post.ApplicationUser.UserName,
// having problem at this line dont know how to construct url at this line as i have to supply url
// (String type to the PostedByAvatar)
PostedByAvatar = db.Files.SingleOrDefault(s => s.ApplicationUserId == post.PostedBy),
PostedDate = post.PostedDate,
PostId = post.PostId,
}).AsEnumerable();
return Json(ret, JsonRequestBehavior.AllowGet);
}
this is the knockout function--------
function Post(data) {
var self = this;
data = data || {};
self.PostId = data.PostId;
self.Message = ko.observable(data.Message || "");
self.PostedBy = data.PostedBy || "";
self.PostedByName = data.PostedByName || "";
self.PostedDate = getTimeAgo(data.PostedDate);
self.PostedByAvatar = data.PostedByAvatar || "";
self.error = ko.observable();
self.PostComments = ko.observableArray();
and this is the view model to get the existing post, comment etc with image from the database-----
function viewModel() {
var self = this;
self.posts = ko.observableArray();
self.newMessage = ko.observable();
self.error = ko.observable();
self.loadPosts = function () {
// to load existing posts
$.ajax({
url: postApiUrl1,
datatype: "json",
contentType: "application/json",
cache: false,
type: 'Get'
})
and on my view page, this the container box to load the image with post-----
<ul id="msgHolder" data-bind="foreach: posts">
<li class="postHolder">
<img data-bind="attr: { src: PostedByAvatar }">
<p><a data-bind="text: PostedByName"></a>: <span data-bind=" html: Message"></span></p>
Now, the model class which saves the image in database is something like this.It has ApplicationUserId as foreign key pointing to ApplicationUserClass---
public class File
{
[Key]
public int FileId { get; set; }
[StringLength(255)]
public string FileName { get; set; }
[StringLength(100)]
public string ContentType { get; set; }
public byte[] Content { get; set; }
public FileType FileType { get; set; }
public int ApplicationUserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
and ApplicationUserClass is something like this---
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ApplicationUser()
{
this.Posts = new HashSet<Post>();
this.Files = new HashSet<File>();
}
public virtual ICollection<File> Files { get; set; }
public virtual ICollection<Post> Posts { get; set; }
This is the image saved in database.Now, i want to know how to construct url for the image saved in the database as i have to supply it to the view model in string form. or there is any approach better than this.
This is my Post class which have many to one relationship with ApplicationUser class and foreign key is PostedBy pointing the ApplicationUser Class----
public class Post
{
public Post()
{
this.PostComments = new HashSet<PostComment>();
}
[Key]
public int PostId { get; set; }
public string Message { get; set; }
public int? PostedBy { get; set; }
public System.DateTime PostedDate { get; set; }
public virtual ICollection<PostComment> PostComments { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Although it is possible to pass a Base64 string as the src of the <img>, I think the most sensible way would be to not return the actual bytes from the Ajax call but rather create a url that will request the image bytes from the server.
First, add the required Action that will serve the image data:
[HttpGet]
public FileResult GetFileData(int fileId)
{
var file = db.Files.Single(x => x.FileId == fileId);
return File(file.Content, file.ContentType);
}
Now, change your GetPosts action to return url in the PostedByAvatar property:
public JsonResult GetPosts()
{
var ret = (from post in db.Posts.ToList()
orderby post.PostedDate descending)
select new
{
Message = post.Message,
PostedBy = post.PostedBy,
PostedByName = post.ApplicationUser.UserName,
PostedByAvatar = _GenerateAvatarUrlForUser(post.PostedBy),
PostedDate = post.PostedDate,
PostId = post.PostId,
});
return Json(ret, JsonRequestBehavior.AllowGet);
}
private string _GenerateAvatarUrlForUser(int? userId)
{
if (!user.HasValue)
return null;
var avatarImage = db.Files.SingleOrDefault(s => s.ApplicationUserId == userId);
if (avatarImage != null)
return Url.Action("GetFileData", new { fileId = avatarImage.FileId });
return null;
}
I'm trying to import data from excel using LinqToExcel. I have few readonly properties in the model class. When I try to map them with the excel columns, they fail with following error. Also when I drop those columns from excel, it works fine without values.
Method 'Total' not found.
Model:Budget
[Required]
[Display(Name = "Room Type")]
public int RoomTypeID { get; set; }
[ForeignKey("RoomTypeID")]
public virtual RoomType RoomType { get; set; }
public decimal Pair { get; set; }
[ExcelColumn("Cost per Room*")]
public decimal CostPerRoom { get; set; }
[NotMapped]
[ExcelColumn("Total")]
[Display(Name = "Total")]
public decimal Total
{
get
{
if (this.RoomType != null)
{
return this.CostPerRoom * this.RoomType.RoomTypeQty * this.Pair;
}
else
{
return 0;
}
}
}
Budget Controller:
public ActionResult ReadFromExcel()
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads/"), fileName);
file.SaveAs(path);
var excel = new ExcelQueryFactory(path);
excel.DatabaseEngine = DatabaseEngine.Ace;
excel.TrimSpaces = LinqToExcel.Query.TrimSpacesType.Both;
var budgets = from c in excel.Worksheet<Budget>("WorksheeName") select c;
foreach (var item in budgets) // This is where it generates the error.
{
}
}
How do I overcome this?