MVC 4 File upload in jquery modal dialog window - asp.net-mvc-4

Im'm working on an MVC/Razor based application
I'm trying to set up a file upload inside a view that is inside a jquery modal dialog box
here's my View code
#using (Html.BeginForm("<MyAction>", "<MyController>", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
<input type="file" id="UploadImage" name="UploadImage" style="width:705px;" />
</div>
<div>
<input id="sbmt" type="submit" value="Save" />
</div>
}
but when i get to my controller action, Request.Files.Count is allways 0
public ActionResult MyAction(Model model){
...
}
What am I missing here?
Thanks

I was able to perform a File Upload in a JQuery Modal Dialog (MVC) by explicitly adding form data to the Ajax Post:
Javascript Code:
// Checking whether FormData is available in browser
if (window.FormData !== undefined) {
var fileUpload = $("#fileInput").get(0);
var files = fileUpload.files;
// Create FormData object
var fileData = new FormData();
// Looping over all files and add it to FormData object
for (var i = 0; i < files.length; i++) {
fileData.append(files[i].name, files[i]);
}
// Adding additional parameters to FormData object
fileData.append('name', $('#nameinput').val());
fileData.append('uniqueID', $('#hiddenFieldUniqueID').val());
$.ajax({
type: 'POST',
contentType: false,
processData: false,
url: '#Url.Action("UploadFile", "YourController")',
data: fileData,
success: function (returnValues) {
$('.ui-dialog-buttonpane').unblock();
if (returnValues["success"] == true) {
bootbox.alert(returnValues["feedback"]);
$(dlg).dialog("close");
}
else {
bootbox.alert(returnValues["feedback"]);
}
},
error: function (returnValue) {
$('.ui-dialog-buttonpane').unblock();
debugger;
bootbox.alert({ message: "Oops - Error Occured!" + returnValue, size: 'small' });
}
});
}
else {
bootbox.alert("Your browser doesnt support the method we are using to upload files (FormData is not supported)");
}
HTML (No form tag required):
<div class="col-md-9">
<label class="btn btn-primary" for="fileInput">
<input id="fileInput" type="file" style="display:none"
onchange="$('#upload-file-info').html(this.files[0].name)">
Select
</label>
<span class='label label-info' id="upload-file-info"></span>
</div>
Controller:
[HttpPost]
public ActionResult UploadFile()
{
YourObjectFile yourObjectFile = null;
try
{
string name = Request.Form["name"];
if (Request.Files.Count > 0)
{
yourObjectFile = new YourObjectFile ();
HttpPostedFileBase file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
string fileName = file.FileName;
using (var reader = new System.IO.BinaryReader(file.InputStream))
{
yourObjectFile.RawData = reader.ReadBytes(file.ContentLength);
}
}
}
.......
Credit for this approach belongs here: http://www.c-sharpcorner.com/UploadFile/manas1/upload-files-through-jquery-ajax-in-Asp-Net-mvc/

Related

How to send upload file to controller - files is always empty

UserAdmin.cshtml
<div class="modal-body">
<form id="upload-file-dialog-form"
class="needs-validation form-group" novalidate
onsubmit="UploadFile()"
enctype="multipart/form-data"
method="post">
<div class="col-md-10">
<p>Upload one or more files using this form:</p>
<input type="file" name="file_Uploader" />
</div>
<div class="form-group">
<div class="col-md-10 modal-footer">
<input type="submit" class="btn btn-primary" value="Upload"/>
</div>
</div>
</form>
</div>
UserAdmin.js
function UploadFile() {
var form = $('form')[0];
var formData = new FormData(form);
console.log(formData);
$.ajax({
url: '/API/Upload',
type: 'POST',
data: formData,
contentType: false,
processData: false,
success: function (data) {
},
error: function () {
}
});
}
Controller
[HttpPost]
public async Task<IActionResult> Upload(List<IFileUpload> files)
{
try
{
var check = (HttpContext.Request.Form.Files);
long size = files.Sum(f => f.Length);
//some code removed
return Ok(new { count = files.Count, size, filePaths });
}
catch (Exception exc)
{
logger.Error("Error in upload() " + exc.Message);
throw;
}
}
the files in controller is always 0.
If onsubmit="UploadFile()" is replaced with
asp-controller="API" asp-action="Upload"
then I get something in check but again converting it to List of IFileUpload is another blocker
First of all, If you want to upload multiple files you have to add multiple="multiple" in your input. FormData will be empty if you print it like this, you have to iterate through the items.
<input type="file" name="file_Uploader" multiple="multiple" />
Please follow the codes below, I tested it working.
Complete form
<form id="upload-file-dialog-form"
onsubmit="UploadFile(event)">
<div class="col-md-10">
<p>Upload one or more files using this form:</p>
<input type="file" name="file_Uploader" multiple="multiple" />
</div>
<div class="form-group">
<div class="col-md-10 modal-footer">
<input type="submit" class="btn btn-primary" value="Upload" />
</div>
</div>
</form>
Construct form data like below
<script>
function UploadFile(e) {
e.preventDefault();
var formData = new FormData($('#upload-file-dialog-form')[0]);
$.each($("input[type='file']")[0].files, function(i, file) {
formData.append('files', file);
});
$.ajax({
url: '/API/Upload',
type: 'POST',
data: formData,
contentType: false,
processData: false,
success: function(data) {
},
error: function() {
}
});
}
</script>
Action method
[HttpPost]
public async Task<IActionResult> Upload(List<IFormFile> files)
{
try
{
var check = (HttpContext.Request.Form.Files);
long size = files.Sum(f => f.Length);
return Ok(new { count = files.Count, size });
}
catch (Exception exc)
{
_logger.LogWarning("Error in upload() " + exc.Message);
throw;
}
}
In model class, use IFormFile
public List<IFormFile> file_Uploader {get;set;}"
In controller, change the parameter like this
public async Task<IActionResult> Upload(List<IFormFile> file_Uploader)
add multiple to upload more files, and keep the name attribute the same as parameter to post value, code like below:
<input type="file" name="file_Uploader" multiple/>
result:

Is there a way for Code Block feature to keep line breaks in CKEditor5 with ASP.Net Core?

I am making a bulletin board system using CKEditor. Most of the features work just fine, but when editing an existing post, the all line breaks in the text are removed from the code block.
Image of create a post
Image of edit a post
Image of part of the response source
I googled as much as possible to solve this problem, but the methods I found were to no avail, so I removed it from the code again.
It seems that line breaks are removed while processing the source internally in CKEditor5, is there any way?
Replace all line breaks with <br /> tags.
Add /\r|\n/g to protectedSource
The following is the view file for that feature.
#model BBSArticleWriteView
#{
// Action name of the current view
var thisActionString = #ViewContext.RouteData.Values["action"].ToString();
if (Model.ArticleId == null)
ViewData["Title"] = "Writing";
else
ViewData["Title"] = "Editing";
}
<p class="page-header">#ViewData["Title"]</p>
<form asp-action="#thisActionString" id="editor-form">
<input asp-for="ArticleId" value="#Model.ArticleId" hidden />
<div>
<input asp-for="Title" required placeholder="Please enter a title." class="form-control w-100 mb-2" />
</div>
<div>
<textarea name="Contents" id="editor">
#Html.Raw(Model.Contents)
</textarea>
</div>
<div>
<input class="btn btn-sm btn-primary" type="submit" value="Save" onsubmit="Editor.submit()" />
<button class="btn btn-sm btn-primary" type="button" href="##" onclick="history.back()">Back</button>
</div>
</form>
<style>
.ck-editor__editable_inline {
min-height: 400px;
}
</style>
#section Scripts {
<script src="~/lib/ckeditor5/ckeditor.js" asp-append-version="true"></script>
<script>
class Editor{
static submit() {
return true;
}
}
ClassicEditor
.create(document.querySelector('#editor'),
{
simpleUpload:{
uploadUrl: "#Url.Action(nameof(CreatorFront.Controllers.FileController.Upload), "File")",
withCredentials: true
},
protectedSource:[
/\r|\n/g
]
})
.catch(error => {
console.error(error);
});
</script>
}
And here is the controller action that puts data into the view model.
[HttpGet]
public async Task<IActionResult> BBSEdit(int id)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
if(user == null)
{
return RedirectToAction("Index", "Home");
}
var article = _cContext.BBSArticle.First(a => a.ArticleId == id);
if(article == null)
{
return RedirectToAction(nameof(BBSList));
}
if(user.Id != article.UserId)
{
return RedirectToAction(nameof(BBSList));
}
var model = new BBSArticleWriteView();
CopyProperties(model, article);
return View(nameof(BBSWrite), model);
}
The following is a function that puts content data in DB.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> BBSWrite(BBSArticleWriteView article)
{
if(ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
if(user == null)
{
RedirectToAction("Index", "Home");
}
// XSS attacks prevent
article.Contents = _htmlSanitizer.Sanitize(article.Contents);
var currentDateTime = DateTime.Now;
CreatorLib.Models.BBS.BBSArticle data = new CreatorLib.Models.BBS.BBSArticle()
{
ArticleId = _cContext.BBSArticle.Max(a => a.ArticleId) + 1,
MainCategory = article.MainCategory,
SubCategory = article.SubCategory,
UserId = user.Id,
Title = article.Title,
Contents = article.Contents,
Status = CreatorLib.Models.BBS.ArticleStatus.A,
IpAddress = HttpContext.Connection.RemoteIpAddress.ToString(),
RegisteredTime = currentDateTime,
LastUpdatedTime = currentDateTime,
HasMedia = article.HasMedia
};
_cContext.BBSArticle.Add(data);
await _cContext.SaveChangesAsync();
return RedirectToAction(nameof(BBSList));
}
return View(article);
}
Here, it is confirmed that HtmlSanitizer has absolutely no impact on this issue.
In DB, line breaks are fully preserved.

Any Idea how to handle file input fields when updating a form

I am using asp.net core 3.1 with Razor forms.
I have a form that contains an input of type file and it is multiple files input.
In the create form it is easy to access the file from the model.
The problem is in the update form how can handle the preview, delete adding new files to the multiple
file input.
Is there a best practice to solve such thing.
The problem is in the update form how can handle the preview, delete adding new files to the multiple file input. Is there a best practice to solve such thing.
I suggest that you could use jQuery MultiFile.
Here are the steps:
1.Download the jQuery MultiFile:https://multifile.fyneworks.com/#Install
2.Find the download zip file and extract it then move to the project wwwroot/lib folder:
For asp.net core mvc:
View:
<form asp-controller="Home" asp-action="UploadData" enctype="multipart/form-data">
<div>
<input type="file" name="files" multiple="multiple" class="multi with-preview" />
<input type="submit" value="Upload" />
</div>
</form>
#section Scripts
{
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js" type="text/javascript" language="javascript"></script>
<script src="~/lib/multifile-master/jquery.MultiFile.js"></script>
}
Controller:
[HttpPost]
public IActionResult UploadData(List<IFormFile> files)
{
//do your stuff...
return Ok();
}
For asp.net core razor pages:
Index.cshtml:
#page
#model IndexModel
<form asp-page-handler="UploadData" enctype="multipart/form-data">
<div>
<input type="file" name="files" multiple="multiple" class="multi with-preview" />
<input type="submit" value="Upload" />
</div>
</form>
#section Scripts
{
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js" type="text/javascript" language="javascript"></script>
<script src="~/lib/multifile-master/jquery.MultiFile.js"></script>
}
Index.cshtml.cs:
public class IndexModel : PageModel
{
public IActionResult OnGet()
{
return Page();
}
public IActionResult OnPostUploadData(List<IFormFile> files)
{
return Page();
}
}
Result:
I've been using bootstrap4 file input!
to load the images when updating the form I used the following way:
var filesArray = [];
$(document).ready(function ()
{
$("#photos").fileinput("refresh",
{
showUpload: false,
showRemove: false
});
loadPhotos();
setTimeout(function ()
{
if (filesArray.length > 0)
{
$(".file-drop-zone-title").remove();
$('#photos').fileinput('readFiles', filesArray);
}
}, 2500);
});
function loadPhotos()
{
//hddPhotos is a hidden input that I stored the images URLs in
var photosPath = $('#hddPhotos').val();
if (photosPath !== null && photosPath!=='')
{
var photos = jQuery.parseJSON($('#hddPhotos').val());
if (photos.length > 0)
{
var count = photos.length;
for (var i = 0; i < count; i++)
{
getBlobofImage(photos[i]);
}
}
}
}
function getBlobofImage(imagePath)
{
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET", imagePath);
xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
xhr.onload = function ()
{
blob = xhr.response;//xhr.response is now a blob object
filesArray.push(new File([blob], /[^/]*$/.exec(imagePath)[0]));
};
xhr.send();
}

I want to show my PDF in partial view where download option are not available its mandatory?

I want to show PDF without Download Option. After many search of Google I get some answer but I'm facing a problem in this.
PDF is open in partial View, but there have also Download Option. Is there another option to open Pdf without Download option?
#model Bizzop.Models.MyAccountModel
#{
Layout = null;
}
<html>
<head>
<title>INDEX</title>
</head>
<body>
<div id="divPartialView">
</div>
<div class="container">
#if (Model.MyAccountList.Count > 0)
{
foreach (var items in Model.MyAccountList)
{
<div class="video-row">
<a href="#" target="_blank" onclick="myPdf(this)"
id="#items.PdfName">
<div class="row">
#if (items.PdfName == "" || items.PdfName == null)
{
<img src="ImageName"/>
}
else
{
<img src="ImageName"/>
}
</div>
</a>
</div>
}
}
</div>
////// This is Ajax code where we pass File name when click the user in anchor
tag
<script>
function myPdf(e) {
debugger
var filen = e.id;
$.ajax({
url: "/MyAccount/MyPdfResult",
data: { pdfname: filen },
cache: false,
type: "POST",
dataType: "html",
type: "post",
success: function (data) {
SetData(data);
},
error: function (data) {
}
});
function SetData(data) {
$("#divPartialView").html(data); // HTML DOM replace
}
}
</script>
/////////// In Controller
public ActionResult MyPdfResult(string pdfname = null)
{
string embed = "<object data=\"{0}\" type=\"application/pdf\"
width=\"500px\" height=\"300px\">";
embed += "</object>";
TempData["Embed"] = string.Format(embed,
VirtualPathUtility.ToAbsolute("~/Content/TutorialImage/TutorialPdf/"+
pdfname));
return PartialView("_Viewpdf", TempData["Embed"]);
}
///// where i am create a Partial View
<div class="ancor">
#Html.Raw(TempData["Embed"])
</div>
i just tried it on my side and it worked for me. You need to make sure that your app is allowed to access the pdf file.
This is my code:
The controller:
[HttpPost]
[AllowAnonymous]
public ActionResult MyPdfResult(string pdfname = null)
{
string embed = "<object data=\"{0}\" type=\"application/pdf\" width=\"500px\" height=\"300px\">";
embed += "If you are unable to view file, you can download from here";
embed += " or download <a target = \"_blank\" href = \"http://get.adobe.com/reader/\">Adobe PDF Reader</a> to view the file.";
embed += "</object>";
TempData["Embed"] = string.Format(embed, VirtualPathUtility.ToAbsolute("~/Files/pdf.pdf"));
return PartialView("_Viewpdf", TempData["Embed"]);
}
The partial view:
<style type="text/css">
body {
font-family: Arial;
font-size: 10pt;
}
#using (Html.BeginForm("MyPdfResult", "Home", FormMethod.Post))
{
View PDF
<hr />
#Html.Raw(TempData["Embed"])
}
The Index:
<div>
#Html.Partial("_Viewpdf");
</div>

knockout.js binding issue when trying to refresh data

I am using knockout.js data binding. At the page load the binding works fine and data is shown on the page correctly. Then I try to push data back to the database and the push is successful. The database receives the data OK.
The problem comes when I try to reload the data upon push success. At this time the binding already happen once (at the page load). If I don't bind it again the data on the page does not refresh. If I do the binding again knockout.js issues an error "cannot bind multiple times". If I do a cleanup before rebinding I receive an error "nodeType undefined".
Can anyone tell me what I have missed here? I am using ASP.NET MVC 4.0 with knockout.js 3.0.0.
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplJSON.Controllers
{
public class KnockoutController : Controller
{
//
// GET: /Knockout/
public ActionResult Index()
{
return View();
}
[HttpGet]
public JsonResult GetProductList()
{
var model = new List<Product>();
try
{
using (var db = new KOEntities())
{
var product = from p in db.Products orderby p.Name select p;
model = product.ToList();
}
}
catch (Exception ex)
{ throw ex; }
return Json(model, JsonRequestBehavior.AllowGet);
}
// your controller action should return a JsonResult. There's no such thing as a controller action that returns void. You have specified dataType: 'json' so return JSON. That's it. As far as what parameter this controller action should take, well, from the JSON you are sending ({"Name":"AA"}) it should be a class that has a Name property of type string.
// modify your action signature to look like this: [HttpPost] public ActionResult SaveProduct (Product product) { ... return Json(new { success = true }); }. Or get rid of this dataType: 'json' attribute from your AJAX request if you don't want to return JSON. In this case you could return simply status code 201 (Created with empty response body): return new HttpStatusCodeResult(201);.
[HttpPost]
public ActionResult SaveProduct(Product product)
{
using (var db = new KOEntities())
{
db.Products.Add(new Product { Name = product.Name, DateCreated = DateTime.Now });
db.SaveChanges();
}
return Json(new { success = true });
}
}
}
View:
#{
ViewBag.Title = "Knockout";
}
<h2>Knockout</h2>
<h3>Load JSON Data</h3>
<div id="loadJson-custom" class="left message-info">
<h4>Products</h4>
<div id="accordion" data-bind='template: { name: "product-template", foreach: product }'>
</div>
</div>
<h3>Post JSON Data</h3>
<div id="postJjson-custom" class="left message-info">
<h4>Add Product</h4>
<input id="productName" /><br />
<button id="addProduct">Add</button>
</div>
#section Scripts {
#Scripts.Render("~/bundles/knockout")
#Scripts.Render("~/bundles/livequery")
<script src="/Scripts/jquery.livequery.min.js"></script>
<style>
#accordion { width: 400px; }
</style>
<script id="product-template" type="text/html">
<h3><a data-bind="attr: {href:'#', title: Name, class: 'product'}"><span data-bind="text: Name"></span></a></h3>
<div>
<p>Here's some into about <span data-bind="text: Name" style="font-weight: bold;"></span> </p>
</div>
</script>
<script>
var isBound;
function loadJsonData() {
$.ajax({
url: "/knockout/GetProductList",
type: "GET",
contentType: "application/json",
dataType: "json",
data: {},
async: true,
success: function (data) {
var loadJsonViewModel = {
product: ko.observableArray(),
init: function () {
loadAccordion();
}
};
var a = $('loadJson-custom');
loadJsonViewModel.product = ko.mapping.fromJS(data);
if (isBound) { }
else
{
ko.applyBindings(loadJsonViewModel, $('loadJson-custom')[0]);
isBound = true;
}
loadJsonViewModel.init();
}
});
}
// push data back to the database
function pushJsonData(productName) {
var jData = '{"Name": "' + productName + '"}';
$.ajax({
url: "/knockout/SaveProduct",
type: "POST",
contentType: "application/json",
dataType: "json",
data: jData,
async: true,
success: function (data, textStatus, jqXHR) {
console.log(textStatus + " in pushJsonData: " + data + " " + jqXHR);
//ko.cleanNode($('loadJson-custom')[0]);
loadJsonData();
},
error: function (jqXHR, textStatus, errorThrown) {
alert(textStatus + " in pushJsonData: " + errorThrown + " " + jqXHR);
}
});
}
function loadAccordion() {
$("#accordion > div").livequery(function () {
if (typeof $("#accordion").data("ui-accordion") == "undefined")
{
$("#accordion").accordion({ event: "mouseover" });
}
else
{
$("#accordion").accordion("destroy").accordion({ event: "mouseover" });
}
});
}
$(function () {
isBound = false;
loadJsonData();
$("#addProduct").click(function () {
pushJsonData($("#productName").val());
});
});
</script>
}
Here is a complete solution for your question.
I have just implemented and checked.
Please have a look.
I have created a class for getting some records ie: Records.cs.
public static class Records
{
public static IList<Student> Stud(int size)
{
IList<Student> stud = new List<Student>();
for (int i = 0; i < size; i++)
{
Student stu = new Student()
{
Name = "Name " + i,
Age = 20 + i
};
stud.Add(stu);
}
return stud;
}
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
Here is a controller for the respective view.
//
// GET: /HelpStack/
private static IList<Student> stud = Records.Stud(10);
public ActionResult HelpStactIndex()
{
return View();
}
[HttpGet]
public JsonResult GetRecord()
{
return Json(stud, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public void PostData(Student model)
{
//do the required code here as All data is in "model"
}
Here is a view as HTML, I have taken two section one for list and other to Add records
<div id="loadJson-custom">
<h4>Student</h4>
<table width="100%">
<tr>
<td style="width: 50%">
<div id="accordion" data-bind='template: { name: "product-template", foreach: Student }'>
</div>
</td>
<td valign="top">
<div id="NewStudent">
<input type="text" data-bind="value: Name" /><br />
<input type="number" data-bind="value: Age" /><br />
<input type="submit" data-bind="click: Save" value="AddNew" />
</div>
</td>
</tr>
</table>
Here is your scripts for Knockoutjs.
<script id="product-template" type="text/html">
<h3><a data-bind="attr: { href: '#', title: Name, class: 'product' }"><span data-bind=" text: Name"></span></a>
<br />
Age: <span data-bind="text: Age"></span>
</h3>
<div>
<p>Here's some into about <span data-bind="text: Name" style="font-weight: bold;"></span></p>
</div>
</script>
<script type="text/javascript">
//Model for insert new record
var Model = {
Name: ko.observable(''),
Age: ko.observable(''),
};
var Student = ko.observableArray([]); // List of Students
function loadData() { //Get records
$.getJSON("/HelpStack/GetRecord", function (data) {
$.each(data, function (index, item) {
Student.push(item);
});
}, null);
}
function Save() { //Save records
$.post("/HelpStack/PostData", Model, function () { //Oncomplete i have just pushed the new record.
// Here you can also recall the LoadData() to reload all the records
//Student.push(Model);
Student.unshift(Model); // unshift , add new item at top
});
}
$(function () {
loadData();
ko.applyBindings(Model, $('#NewStudent')[0]);
});
</script>
You are declaring your model inside loadJsonData function success callback, & creating new object on every success callback, move the model outside that function, create an object & use it inside loadJsonData function, it will fix the issue.