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
Related
I have a problem all the time filename returns null in controller.There are no options for how to solve this, I reviewed the entire Internet
Screen: https://drive.google.com/file/d/1p6om2yBJ8sED_8DP0yB1AKEPYlcFXq_A/view?usp=sharing
][2]
public int CategoryId { get; set; }
[Required(ErrorMessage = "Enter category name")]
[MaxLength(100, ErrorMessage = "MaxLength 100 symbols")]
[MinLength(5, ErrorMessage = "MinLength 5 symbols")]
public string CategoryName { get; set; }
[Required(ErrorMessage = "Enter category description")]
[MaxLength(500, ErrorMessage = "MaxLength 500 symbols")]
[MinLength(100, ErrorMessage = "MaxLength 100 symbols")]
public string CategoryDiscription { get; set; }
public string FileName { get; set; }
[NotMapped]
public IFormFile File { get; set; }
public async Task<ActionResult> Create([Bind("CategoryId","CategoryName","formFile")] CategoriesModel categories)
{
try
{
string rootpatch = _webHostEnvironment.WebRootPath;
string filename = Path.GetFileNameWithoutExtension(categories.File.FileName);
string extension = Path.GetExtension(categories.File.FileName);
categories.FileName = filename + DateTime.Now.ToString("yy.MMM.dd") + extension;
string patch = Path.Combine(rootpatch + "/image/", filename);
using (var fileStream = new FileStream(patch, FileMode.Create))
{
await categories.File.CopyToAsync(fileStream);
}
_context.Add(categories);
await _context.SaveChangesAsync();
return RedirectToAction();
}
catch
{
return View();
}
}
<form asp-action="Create" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="File" class="control-label"></label>
<input asp-for="File" class="form-control" type = "file" accept = "image/*"/>
<span asp-validation-for="File" class="text-danger"></span>
</div>
</form>
You use [Bind("CategoryId","CategoryName","formFile")] ,so the File property will not be binded to CategoriesModel categories.Here is the official doc about Bind attribute.
You can try to remove [Bind("CategoryId","CategoryName","formFile")],and make sure the name of Input is File in your view.
On create action i'm allowing the user to upload a picture file, and based on the following view model if it is invalid during post action the message appear and the model is repopulated again with user's input except the file.
i'm not sure if i can repopulate the file when the model is invalid
ViewModel
public class CreateEditChildViewModel
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string DateOfBirth { get; set; }
[Required]
public string Gender { get; set; }
[Required]
public string EducationalLevel { get; set; }
public string Picture { get; set; }
public string Country { get; set; }
public string Region { get; set; }
public string City { get; set; }
}
Post Action
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateEditChildViewModel model)
{
if (ModelState.IsValid)
{
string webRootPath = _hostingEnvironment.WebRootPath;
var files = HttpContext.Request.Form.Files;
var child = new Childs()
{
FirstName = model.FirstName,
LastName = model.LastName,
ParentOfId = model.ParentId,
SpecialistOfId = model.SpecialistId,
DateOfBirth = model.DateOfBirth,
Gender = model.Gender,
...................
};
_db.Childs.Add(child);
await _db.SaveChangesAsync();
if (files.Count() > 0)
{
var uploads = Path.Combine(webRootPath, "images");
var extension = Path.GetExtension(files[0].FileName);
using (var filesStream = new FileStream(Path.Combine(uploads, child.Id + extension), FileMode.Create))
{
files[0].CopyTo(filesStream);
}
child.Picture = #"\images\" + child.Id + extension;
}
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
CreateEditChildViewModel modelVM = new CreateEditChildViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
....................
//All model properties
};
return View(modelVM);
}
To sum up my question i would like to save the file input in case if the model is invalid i don't make the user to re-upload the file
You can use Ajax partial refresh to upload data to the backend, which will keep the state of the client page. The error can be judged by h5 or customlize error.
Here is the example(take the FirstName and LastName as examples).
ViewModel(Add File attribute):
public class CreateEditChildViewModel
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
//...
public IFormFile File { get; set; }
}
Your View:
<form method="post" id="myForm" enctype="multipart/form-data">
<div id="error"></div>
<div asp-validation-summary="All"></div>
<label asp-for="#Model.FirstName"></label>
<input asp-for="#Model.FirstName" />
<label asp-for="#Model.LastName"></label>
<input asp-for="#Model.LastName" />
<input asp-for="#Model.File" />
<input type="submit" value="submit" id="sub" />
</form>
#section Scripts{
<script>
$('#myForm').submit(function (e) {
e.preventDefault();
var formData = new FormData($('#myForm')[0]);
$.ajax({
method: 'post',
url: "/Home/Create",
data: formData,
processData: false,
contentType: false,
success: function (data) {
console.log(data)
if (data == 'Index') {
location.href = '/home/index'
} else {
$('#error').text('please fill in valid data')
}
},
error: function (e) {
}
})
})
</script>
}
Post Action(Change the return data of the background to json):
[HttpPost]
public async Task<IActionResult> Create(CreateEditChildViewModel model)
{
if (ModelState.IsValid)
{
string webRootPath = _hostingEnvironment.WebRootPath;
var files = HttpContext.Request.Form.Files;
var child = new Childs()
{
FirstName = model.FirstName,
LastName = model.LastName,
//...................
};
_db.Childs.Add(child);
await _db.SaveChangesAsync();
if (files.Count() > 0)
{
var uploads = Path.Combine(webRootPath, "images");
var extension = Path.GetExtension(files[0].FileName);
using (var filesStream = new FileStream(Path.Combine(uploads, child.Id + extension), FileMode.Create))
{
files[0].CopyTo(filesStream);
}
child.Picture = #"\images\" + child.Id + extension;
}
await _db.SaveChangesAsync();
return Json("Index");
}
CreateEditChildViewModel modelVM = new CreateEditChildViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
//....................
};
return Json(modelVM);
}
Result:
I pieced together this CSV parser, I chose oleDB as of all the ones I read it was the one I could make sense of. I have a client model I think I need to call it in the parser but I'm unsure. I get this error and think it may it that? How can I reference the model??
System.NullReferenceException: 'Object reference not set to an instance of an object.'
on this line
using (OleDbConnection cn = new OleDbConnection(csv.ConnectionString))
I don't want to rewrite the model again here though I'm unsure how to call it.
public ActionResult CreateBulk(HttpPostedFileBase attachmentcsv)
{
ConnectionStringSettings csv = ConfigurationManager.ConnectionStrings["csv"];
using (OleDbConnection cn = new OleDbConnection(csv.ConnectionString))
{
cn.Open();
using (OleDbCommand cmd = cn.CreateCommand())
{
cmd.CommandText = "SELECT * FROM [attachmentcsv]";
cmd.CommandType = CommandType.Text;
using (OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
int clientN = reader.GetOrdinal("ClientN");
int homePage = reader.GetOrdinal("homePage");
int clientEmail = reader.GetOrdinal("clientEmail");
int contName = reader.GetOrdinal("contName");
int monthlyQuota = reader.GetOrdinal("monthlyQuota");
int MJTopicsID = reader.GetOrdinal("MJTopicsID");
foreach (DbDataRecord record in reader)
{
String Strip = record.GetString(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 newclient = new Client { clientN = record.GetString(clientN), homePage = Metric[0], clientEmail = record.GetString(clientEmail), contName = record.GetString(contName).First().ToString().ToUpper() + record.GetString(contName).Substring(1), monthlyQuota = record.GetInt32(monthlyQuota), TrustFlow = Int32.Parse(Metric[1]), CitationFlow = Int32.Parse(Metric[2]), RI = Int32.Parse(Metric[3]), MJTopicsID = record.GetInt32(contName), UserTableID = 1 };
db.Clients.Add(newclient);
db.SaveChanges();
}
}
return Redirect("Index");
}
}
}
Form View:
#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>
}
</div>
Client Model
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; }
}
}
In your configuration file (usually App.Config or Web.config depending on the project type) you should have a connectionStrings section:
<configuration>
<connectionStrings>
<add name="Name" providerName="System.Data.ProviderName" connectionString="Valid Connection String;" />
</connectionStrings>
</configuration>
Make sure that the name of one of your connection strings is "csv". The null reference exception you are getting is because nothing is being found in the following line of code:
ConnectionStringSettings csv = ConfigurationManager.ConnectionStrings["csv"];
And so when you try to access a property of csv in:
csv.ConnectionString
the exception occurs.
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;
}
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();