I'm really a begginer at programing so ease stuff are hard for me if can anyone explain me or help me in my situation I would apreciate a lot.
On my create method I need to save a file (pdf in db).
What I have now:
Model:
public class Candidate : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string Profile { get; set; }
public Byte[] CV { get; set; }
}
Controller:
[HttpPost("UploadFiles")]
public 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 MemoryStream())
{
files.CopyTo(stream);
//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 });
}
In my view i'm using this to attach the file:
<form method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-action="Index">
<div class="form-group">
<div class="col-md-10">
<p>Upload one or more files using this form:</p>
<input type="file" name="files" multiple />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Upload" />
</div>
</div>
</form>
You're not going to get a file path to work with. An uploaded file would be in the HTTP request, but you haven't set your action method up to accept that properly. You really should read some tutorials about how to upload files in ASP.NET MVC so you have the general idea, rather than asking someone on Stack Overflow to explain what you need to do.
#mason.
This is not a complete answer to your problem but it will show you how file uploading works in asp.net mvc. You will have to modify it according to your problem.
Here’s a form that will post back to the current action.
#using (Html.BeginForm("Upload", "Upload", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" />
<input type="submit" name="Submit" id="Submit" value="Upload" />
}
Here’s the action method that this view will post to which saves the file into a directory in the App_Data folder named “uploads”.
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file) {
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
Visit this for complete solution
Again this answer is only to understand how we made HTTP request to upload files and how to save files.
Related
I want to upload my files to the filesystem after I have pressed the "save" button in the Edit view. To do that I am trying to call the UploadToFileSystem method inside the Edit (POST) action.
I learned how to do that here but this tutorial shows you how to do it in the Index action grid view. I have been trying to reverse engineer that logic so as to do it in the edit action.
This is what it looked like before I call UploadToFileSystem in a button that uses only that method and it worked with saving the path to the file system but not the database.
I would give you a Github link to the app but it has lots of dependencies that you would need to install and gain access to.
BEFORE:
Debugging in VS Code showing UploadToFileSystem being called by itself.
AFTER: (Where it didn't work.)
Debugging in VS Code showing UploadToFileSystem being called by itself.
So after that error in the second image showing there were no files being retrieved I have tried to use a view model called ProblemInputModel but I get a null reference exception once the file gets passed to the view.
ProblemsController.cs
// POST: Problems/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,ProblemTitle,ProblemDescription,ProblemStartDate,ProblemFileAttachments,ProblemSeverity,ProblemComplete")] Problem problem, List<IFormFile> iFormFile)
{
//Used for file attachment upload.
if (id != problem.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(problem);
await _context.SaveChangesAsync();
//Upload or update any attachments user inserted.
await UploadToFileSystem(iFormFile ,problem.ID, problem.ProblemTitle, problem.ProblemDescription,
problem.ProblemStartDate, problem.ProblemSeverity);
}
catch (DbUpdateConcurrencyException)
{
if (!ProblemExists(problem.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(problem);
}
[HttpPost]
public async Task<IActionResult> UploadToFileSystem(List<IFormFile> files, int? id, string title,
string description, DateTime dateTime, int severity)
{
foreach (var file in files)
{
//Get the base Path, i.e, The Current Directory of the application + /Files/.
var basePath = Path.Combine(Directory.GetCurrentDirectory() + "\\Files\\");
//Checks if the base path directory exists, else creates it.
bool basePathExists = System.IO.Directory.Exists(basePath);
if (!basePathExists) Directory.CreateDirectory(basePath);
//Gets the file name without the extension.
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
//Combines the base path with the file name.
var filePath = Path.Combine(basePath, file.FileName);
//If the file doesnt exist in the generated path, we use a filestream object, and create a new file, and then copy the contents to it.
var extension = Path.GetExtension(file.FileName);
if (!System.IO.File.Exists(filePath))
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
//Create a new Problem object with required values.
var problem = await _context.Problems.FindAsync(id);
problem = new Problem
{
ProblemFileAttachments = filePath,
ProblemTitle = title,
ProblemDescription = description,
ProblemStartDate = dateTime,
ProblemSeverity = severity
};
//Inserts this model to the db via the context instance of EF Core.
_context.Problems.Add(problem);
_context.SaveChanges();
}
}
//Loads all the File data to an object and sets a message in the TempData.
TempData["Message"] = "File successfully uploaded to File System.";
return RedirectToAction("Index");
}
Edit.cshtml
#model Pitcher.Models.Problem
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Problem</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit" enctype="multipart/form-data" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ID" class="control-label" ></label>
<input asp-for="ID" readonly="true" disabled="true" class="form-control"/>
</div>
<div class="form-group">
<label asp-for="ProblemTitle" class="control-label"></label>
<input asp-for="ProblemTitle" class="form-control" />
<span asp-validation-for="ProblemTitle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProblemDescription" class="control-label"></label>
<textarea asp-for="ProblemDescription" class="form-control" rows="10" cols="50"></textarea>
<span asp-validation-for="ProblemDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProblemStartDate" class="control-label"></label>
<input asp-for="ProblemStartDate" class="form-control" />
<span asp-validation-for="ProblemStartDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProblemFileAttachments" class="control-label"></label>
<input asp-for="ProblemFileAttachments" type="file" name="files"/>
#* <button type="submit" class="btn btn-primary" asp-controller="Problems" asp-action="UploadToFileSystem">Upload to File System</button> *#
</div>
<div class="form-group">
<label asp-for="ProblemSeverity" class="control-label"></label>
<select asp-for="ProblemSeverity" class="form-control">
<option value="">Choose severity level</option>
<option value="1">Very Low</option>
<option value="2">Low</option>
<option value="3">Medium</option>
<option value="4">High</option>
<option value="5">Very High</option>
</select>
<span asp-validation-for="ProblemSeverity" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="ProblemComplete" /> #Html.DisplayNameFor(model => model.ProblemComplete)
</label>
</div>
<div class="form-group" method="post">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Problem.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace Pitcher.Models
{
public class Problem
{
public int ID {get;set;}
[Required]
[StringLength(180, MinimumLength = 2, ErrorMessage = "Problem Title must be bettween 2 to 20 characters.")]
[DataType(DataType.Text)]
[Display(Name = "Problem Title")]
[Column("ProblemTitle")]
public string ProblemTitle {get;set;}
[Required]
[StringLength(int.MaxValue, MinimumLength = 5, ErrorMessage = "Problem Title must be at least 5 characters.")]
[DataType(DataType.Text)]
[Display(Name = "Problem Description")]
[Column("ProblemDescription")]
public string ProblemDescription {get;set;}
[Required]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = " Problem Start Date")]
[Column("ProblemStartDate")]
public DateTime ProblemStartDate {get;set;}
[DataType(DataType.Upload)]
[Display(Name = " Upload file")]
[Column("ProblemFileAttachments")]
public string ProblemFileAttachments {get;set;}
[Required]
[Display(Name = "Problem Severity")]
[Range(1,5, ErrorMessage
= "Problem Severity value for {0} must be between {1} and {2}.")]
[Column("ProblemSeverity")]
public int ProblemSeverity {get;set;}
[Display(Name = "Problem Complete")]
[Column("ProblemComplete")]
public bool ProblemComplete {get;set;}
public ICollection<Result> Result {get;set;}
public ICollection<Chat> Chat {get;set;}
//public List<Problem> ProblemFileModel {get; set;}
}
}
ProblemInputModel
//This is a view model only.
public class ProblemInputModel
{
[DataType(DataType.Upload)]
[Display(Name = " Upload file")]
[Column("ProblemFileAttachments")]
public List<IFormFile> ProblemFileAttachments {get;set;}
}
For the two situations, both of them get null value are caused by model binding failure.
You need know two things below:
ASP.NET Core Tag Helper asp-for will generate the id and name attribute.
Model Binding system looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.
Model binding failure here is because the name attribute does not match the parameter name.
For the first situation to directly call UploadToFileSystem method, you get the null parameters:
[HttpPost]
public IActionResult UploadToFileSystem(List<IFormFile> files, int? ID, string ProblemTitle,
string ProblemDescription, DateTime ProblemStartDate, int ProblemSeverity)
{
//.......
return RedirectToAction("Index");
}
For the second situation to call the UploadToFileSystem method inside the Edit action:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, [Bind("ID,ProblemTitle,ProblemDescription,ProblemStartDate,ProblemFileAttachments,ProblemSeverity,ProblemComplete")] Problem problem,
List<IFormFile> files) //change here...
{
//.......
return View(problem);
}
If you want to use ProblemInputModel model, you need firstly change the view code to remove the name="files", then asp-for will generate the name="ProblemFileAttachments" which matches the ProblemFileAttachments property in ProblemInputModel model:
<input asp-for="ProblemFileAttachments" type="file" #*name="files"*#/>
Then remember to add the ProblemInputModel model as a parameter:
public IActionResult Edit(int id, [Bind("ID,ProblemTitle,ProblemDescription,ProblemStartDate,ProblemFileAttachments,ProblemSeverity,ProblemComplete")] Problem problem,
ProblemInputModel model)
I am creating an application where a user can post, something like a forum. I am trying to load an image and text into the database, for that, I have two tables and they are both related through an id. I figured out how to load text content, but I really don't know how to do the same with image content.
I'm using an MVC view with it respective controller to consume the API.
The model I'm using:
public class ForumThemeModel
{
public ForumThemeModel()
{
Themes = new HashSet<Theme>();
}
//forum table
[Key]
public int IdForum { get; set; }
public string PostTittle { get; set; }
//theme table
public int? Idtheme { get; set; }
public string TextContent { get; set; }
public int? IdForum { get; set; }
public IFormFile ContentFile { get; set; } //where the image will be stored
public string FileNameA { get; set; }
public string FileType { get; set; }
}
The API controller where the post creation is made:
[HttpPost]
[Consumes("multipart/form-data")]
[Route("createPost")]
public async Task<IActionResult> createPost([FromBody]ForumThemeModel model)
{
Forum forum = new Forum();
Theme theme = new Theme();
var fileName = Path.GetFileName(model.FileNameA);
var fileExt = Path.GetExtension(fileName);
var newFileName = String.Concat(Convert.ToString(Guid.NewGuid()), fileExt);
using(var target = new MemoryStream())
{
await model.ContentFile.CopyToAsync(target);
theme.ContentFile = target.ToArray();
}
forum.PostTittle = model.PostTittle;
theme.FileNameA = newFileName;
theme.FileType = fileExt;
var postTittle = new SqlParameter("#postTittle", forum.PostTittle);
var textContent = new SqlParameter("#textContent", theme.TextContent);
var content_file = new SqlParameter("#content_file", theme.ContentFile);
var file_name_a = new SqlParameter("#file_name_a", theme.FileNameA);
var FileType = new SqlParameter("#FileType", theme.FileType);
//saves everything to database
return Ok();
}
The MVC controller where the API is used:
[HttpPost]
public IActionResult createPost(ForumThemeModel model)
{
HttpClient hc = new HttpClient();
hc.BaseAddress = new Uri("https://localhost:44325/api/Users");
var userPost = hc.PostAsJsonAsync<ForumThemeModel>("Users/createPost", model);
userPost.Wait();
//do something when the resquest result is successful
}
The view of the MVC controller:
#model Huerbog.Models.Request.ForumThemeModel
#{
ViewData["Title"] = "CreatePost";
}
<hr />
<div class="row">
<div class="col-12 col-md-9">
<form enctype="multipart/form-data" asp-action="createPost">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group h4">
<label asp-for="PostTittle" class="control-label">Tittle</label>
<input asp-for="PostTittle" class="form-control" />
<span asp-validation-for="PostTittle" class="text-danger"></span>
</div>
<div class="form-group h4">
<label asp-for="Content" class="control-label">Content of the post</label>
<textarea rows="5" asp-for="Content" class="form-control"></textarea>
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<div class="form-group h4">
<label asp-for="ContentFile" class="control-label">Imagen:</label><br />
<input class="form-control-file" multiple asp-for="ContentFile" type="file" />
</div>
<div class="form-group h4">
<input type="submit" value="Create post" class="btn btn-primary" />
</div>
</form>
</div>
</div>
So I have read about uploading images or files and it indicates that the attribute enctype = "multipart / form-data" should be present in the view when uploading images or different types of files in a database, due to that attribute two things happen: the first is that when I leave the attribute in the view the MVC controller does not use the API controller, but the model receives the information from the file or image such as the name, the file type, etc. The second is when I remove the attribute from the view, in this case the MVC controller makes use of the API controller, but the model does not contain anything regarding the file or the image. In both cases the text content is received by the model, but it is also not saved by the database due to the lack of information regarding the image.
I'm not very sure if the enctype = "multipart / form-data" atribute is the main reason, but as I said before, the text content, before the image upload, was working well and the enctype = "multipart / form-data" was not present.
I've stuck on the img upload for a while and really I don't know how to do it, any help is appreciated.
public IFormFile ContentFile { get; set; }
Firstly, please note that PostAsJsonAsync method would serialize the model data as JSON then send in the request body, which does not make sense to serialize a FormFile, it would cause an error.
To achieve the requirement of consuming an API from MVC controller action to upload file(s) with other data, you can refer to the following example code.
[HttpPost]
public async Task<IActionResult> CreatePost(ForumThemeModel model)
{
var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(model.PostTittle), "PostTittle");
//...
//for other properties, such as FileNameA, FileType etc
//...
formContent.Add(new StreamContent(model.ContentFile.OpenReadStream()), "ContentFile", Path.GetFileName(model.ContentFile.FileName));
HttpClient hc = new HttpClient();
hc.BaseAddress = new Uri("https://localhost:44325/api/Users/");
var userPost = await hc.PostAsync("createPost", formContent);
//...
API action
public async Task<IActionResult> createPost([FromForm]ForumThemeModel model)
{
//...
I have a ASP.NET MVC 4 Project, and I need to be able to upload a file or files to save them and make an instance of that file or files( so I will be able to keep information about them).
In order to implement this I have two classes inside of 'Models':
1. UploadFile: (this class will represent a single file)
public class UploadFile
{
public long ID { get; set; }
public string Path { get; set; }
public string Status { get; set; }
public HttpPostedFileBase File { get; set; }
}
2. Scan: (this class will represent a one upload of file or files)
public class Scan
{
public IEnumerable<UploadFile> Files { get; set; }
public long ContentLength()
{
long size = 0;
foreach (UploadFile f in Files)
{
size =+ f.File.ContentLength;
}
return size;
}
}
also I have one controller 'HomeController.cs", and I have this action in there:
[HttpPost]
public ActionResult Index(Scan scan)
{
if (scan.ContentLength() > 0)
{
foreach (var s in scan.Files)
{
var fileName = Path.GetFileName(s.File.FileName);
var path = Path.Combine(Server.MapPath("~/Content/UploadFiles"), fileName);
s.File.SaveAs(path);
}
}
return RedirectToAction("Index");
}
And the View is 'index.cshtml', and this is the begin.form:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr>
<td>File:</td>
<td><input type="file" name="Scan" id="Scan" multiple /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" name="submit" value="Upload" /></td>
</tr>
</table>
}
</div>
My Main Goal: upload a file/files (save them at "~/Content/UploadFiles") and during this process to create an instance of UploadFile and Scan (Scan hold a collection of UploadFile), so i can represent them to the user and keep tracking and catalog them in my DB.
My problem is that "scan" is null in 'homecontroller.cs' how can i pass argument so i could create instances of the two classes and still upload the file ?
As you may notice i'm new at MVC design-structure, so if i'm missing a point or two of MVC because of my implement let me know.
Thanks.
I have an MVC3 form bound to a model with a file upload control. (Extra HTML removed for brevity):
#model Models.MessageModel
<script type="text/javascript">
var numAttachments = 0;
$(function () {
$(".add-attachment").click(function () {
$(".attachments").append("<div><input type=\"file\" name=\"attachments\" id=\"attachment" + numAttachments + "\" /></div>");
});
});
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<div class="field-label">Subject:
#Html.EditorFor(model => model.Subject)
</div>
<div class="attachments">
</div>
<div>
Add Attachment
</div>
<div class="message-text">#Html.TextAreaFor(model => model.Text, new { cols = 107, rows = 10 })</div>
<input type="submit" value="Send Message" />
</div>
}
Users can choose to add multiple attachments by clicking the "add attachment" link, attachments are not required.
My model is as follows:
public class MessageModel
{
[Required]
public string Subject { get; set; }
[Required]
public string Text { get; set; }
public IEnumerable<HttpPostedFileBase> Attachments { get; set; }
}
(NOTE: I've also tried moving the attachments out of the model, into an argument to my action method with the same results)
My Action:
[HttpPost]
public ActionResult New(MessageModel message)
{
// this check passes if no file is uploaded
// but once a file is uploaded, this evaluates to false
// even if the model is valid
if (ModelState.IsValid)
{
// do stuff
}
}
This form works fine and validation passes when no file is selected for upload. When I choose a file for upload, ModelState.IsValid becomes false. How can I cause validation to ignore uploaded files?
You need to make sure your form is using the correct "enctype".
#using (Html.BeginForm("New", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
MVC 3 file upload and model binding
#model Framely2011.Models.PictureUpload
#{
ViewBag.Title = "Upload";
}
<h2>Upload</h2>
#using (Html.BeginForm("Upload", "Member", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="myFile" id="myFile" />
#Html.TextBoxFor(x => x.MetaTagsObj.Meta1)<br />
#Html.TextBoxFor(x => x.MetaTagsObj.Meta2)<br />
#Html.TextBoxFor(x => x.MetaTagsObj.Meta3)<br />
<input type="submit" value="submit" />
}
This is what I have so far, here is what my model looks like:
public class PictureUpload
{
public HttpPostedFile fileName { get; set; }
public MetaTags MetaTagsObj { get; set; }
}
I am not sure how to write my controller for the picture upload or how to upload the file at the controller when I do a [HttpPost]
Here is how I have done it in the past (asp.net mvc 2)... There may very well be a much easier way to do it with .net 4 and asp.net mvc 3
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string mimeType = Request.Files[upload].ContentType;
Stream fileStream = Request.Files[upload].InputStream;
string fileName = Path.GetFileName(Request.Files[upload].FileName);
int fileLength = Request.Files[upload].ContentLength;
byte[] fileData = new byte[fileLength];
fileStream.Read(fileData, 0, fileLength);
//do something with the byte array (filedata)
}
HasFile() is an extension method defined like:
public static class HttpPostedFileBaseExtensions
{
public static bool HasFile(this HttpPostedFileBase file)
{
return (file != null && file.ContentLength > 0);
}
}
enter code here
You can just take your model as a parameter in the [HttpPost] action method and read the fileName property.