Problem: Can't parse File object to JSON string, when doing that only null values are gotten, while trying to send complex type to MVC controller.
I tried adding it into FormData object and passing it to controller, however, passing List of them was not successful, because it would either return an empty array or just plain null
model:
public class UploadedDocument
{
public HttpPostedFile File { get; set;}
public string DocumentId { get; set;}
public string DocumentType { get; set; }
}
controller:
[HttpPost]
[ActionName("UploadFile")]
public ActionResult Upload(IEnumerable<UploadedDocument> documents)
{
return View();
}
upload function:
var _documents = [];
for (var i = 0; i < arrayOfFiles.length; i++) {
var document = {
"File": arrayOfFiles[i].file,
"DocumentId": arrayOfFiles[i].documentId,
"DocumentType": arrayOfFiles[i].documentName
};
_documents.push(document);
}
$.ajax({
url: "#Url.Action("UploadFile", "Home")",
type: "POST",
data: {"documents":_documents}
});
}
});
Basically, I manage to handle single upload via ajax like below
Input Element
<input type="file" name="customerpicture" id="customerpicture" />
Formdata
function getFormData() {
var data = new FormData();
var files = $("#customerpicture").get(0).files;
if (files.length > 0) {
data.append("file", files[0]);
}
//data.append("Name", $("#name").val());
return data;
}
Ajax Method
$('#InsertCustomer').click(function () {
debugger;
var antiForgeryToken = $("input[name=__RequestVerificationToken]").val();
var url = '#Url.Action("Add_Customer", "Customer")';
$.ajax({
type: 'POST',
headers: { "__RequestVerificationToken": antiForgeryToken },
url: url,
contentType: false,
processData: false,
data: getFormData(),
success: function (res) {
$('#custinsertmodal').modal('hide');
$('#custinsertmodal').find("input,textarea,select")
.val('')
.end()
.find("input[type=checkbox], input[type=radio]")
.prop("checked", "")
.end();
bootbox.alert({ message: res.result });
}
});
});
Controller
[HttpPost, ValidateHeaderAntiForgeryToken]
public JsonResult Add_Customer()
{
var errMsg = string.Empty;
byte[] tmpImage;
try
{
//Customer Image Processing
var file = Request.Files.Get("file");
if (file != null && file.ContentLength > 0)
{
//Image Saving to Folder
UploadHelper.UploadFile(file);
//Image Saving to Database
tmpImage = new byte[file.ContentLength];
file.InputStream.Read(tmpImage, 0, file.ContentLength);
CustomerModel model = new CustomerModel
{
Signature = tmpImage
};
_setupRepo.CreateSignatory(model);
return Json(new { error = false, result = $"Customer was successfully created" }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
errMsg = ex.Message.ToString();
return Json(new { error = true, result = errMsg }, JsonRequestBehavior.AllowGet);
}
}
If you want to specifically use an ajax call to upload files: you need to use a FormData object. Files must be sent up as a single item within the FormData object and therefore cannot be passed to your ActionResult as part of a list.
Assuming you have a dynamic amount of file inputs on your page with custom fields the user can fill in, your code could look like the following:
HTML / Javascript:
<form id="File_Form">
<input type="file" name="File_1" />
<input type="text" name="DocumentName_File_1" value="doc1" />
<input type="text" name="DocumentId_File_1" value="1" />
<input type="file" name="File_2" />
<input type="text" name="DocumentName_File_2" value="doc2" />
<input type="text" name="DocumentId_File_2" value="2" />
<button>Upload Files</button>
</form>
<script>
$("#File_Form").submit(function() {
var formData = new FormData(this);
$.ajax({
url: '#Url.Action("UploadFiles")',
type: 'POST',
data: formData,
processData: false,
contentType: false,
cache: false
});
return false;
});
</script>
C#:
[HttpPost]
public ActionResult UploadFiles() {
foreach (string fileName in Request.Files) {
HttpPostedFileWrapper file = Request.Files[fileName];
string documentName = Request.Form[$"DocumentName_{fileName}"]?.ToString();
string documentId = Request.Form[$"DocumentId_{fileName}"]?.ToString();
// Do things with your file here.
}
return new HttpStatusCodeResult(System.Net.HttpStatusCode.OK);
}
It may not automatically serialize into your model object, but you can still obtain the result you want with clever naming of your form elements.
Try this.
var _documents = [];
for (var i = 0; i < arrayOfFiles.length; i++) {
var document = {
"File": arrayOfFiles[i].file,
"DocumentId": arrayOfFiles[i].documentId,
"DocumentType": arrayOfFiles[i].documentName
};
_documents.push(document);
}
var formData = new FormData();
formData.append("documents", documents);
$.ajax({
url: "#Url.Action("UploadFile", "Home")",
type: "POST",
data: formData,
processData: false,
contentType: false,
});
}
});
Related
I have a requirement of sending invitations to candidates where a user selects the excel file, transfers it from ajax to controller and validates its size, type etc. Then user clicks on Send Invite button and sends the email invites(having excel file). Please find the below code for reference:
<button type="button" id="bulkuploadButton">Bulk Email Upload</button>
<input type="file" id="ExcelFile" name="ExcelFile" style="display:none" onchange="UploadFile();" onselect="UploadFile();" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" />
Jquery:
function UploadFile() {
if (ValidateExcelFile()) {
var excelFile = document.getElementById('ExcelFile');
formData = new FormData();
if (excelFile.files.length > 0) {
for (var i = 0; i < excelFile.files.length; i++) {
formData.append('file-' + i, excelFile.files[i]);
}
}
$.ajax({
url: url here,
type: "POST",
dataType: 'json',
processData: false,
contentType: false,
data: formData,
success: function (data) {
// Further Processing
},
error: function (err) {
//Error
}
});
}
}
Controller:
[HttpPost]
public JsonResult MyController(HttpPostedFileBase excelFile)
{
if (Request.Files.Count > 0)
{
foreach (string file in Request.Files)
{
excelFile = Request.Files[file];
}
var result = //Call Model here for validation checks and return error msges
TempData["ExcelFile"] = excelFile; //Store in TempData for further processing
return Json(result);
}
return null;
}
The validations are done successfully, now its time to send invite to candidates as:
<button onclick="SendInvite">Send Invitations</button>
Jquery:
function SendInvite() {
//Check validations for other inputs on the page
//Get the excel file same as above
var excelFile = document.getElementById('ExcelFile');
formData = new FormData();
if (excelFile.files.length > 0) {
for (var i = 0; i < excelFile.files.length; i++) {
formData.append('file-' + i, excelFile.files[i]);
}
}
$.ajax({
type: "POST",
url: url here,
data: JSON.stringify({
myModel: myModel,
excelFile: formData
}),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
},
error: function (data) {
}
});
}
Controller:
public JsonResult MyController2(MyModel myModel, HttpPostedFileBase excelFile)
{
//I tried the same method to get the file but it didn't help me
if (Request.Files.Count > 0) //Here Request.Files.Count = 0, it should be = 1 instead
{
foreach (string file in Request.Files)
{
excelFile = Request.Files[file];
}
}
//I then tied to use TempData but it does not have the excel data
excelFile = TempData["ExcelFile"] as HttpPostedFileBase;
//Further processing
I am getting this error while fetching data from TempData. ContentLength is 0 and also the data attribute is null
The data in TempData should be something like this (where ContentLength !=0
and data attribute has some value):
Can anyone help me get the excel data in controller MyController2.
Change the function SendInvite() as:
function SendInvite() {
//Check validations for other inputs on the page
//Get the excel file same as above
var excelFile = document.getElementById('ExcelFile');
formData = new FormData();
formData.append("data", JSON.stringify(myModel));
for (var i = 0; i < excelFile.files.length; i++) {
var file = ExcelFile.files[i];
formData.append("excelFile", file);
}
$.ajax({
type: "POST",
url: url here,
data: formData,
contentType: "application/json; charset=utf-8",
dataType: "json",
contentType: false,
processData: false,
success: function (data) {
},
error: function (data) {
}
});
}
and in controller
public JsonResult MyController2(string data, HttpPostedFileBase[] excelFile)
{
MyModel myModel = JsonConvert.DeserializeObject<MyModel>(data);
if (Request.Files.Count > 0)
{
//Do data processing here
}
//Further processing
Check this link How to Pass Image File and Form Data From Ajax to MVC Controller
I am implementing a feature which allows a user to upload a file which is processed server-side with some information returned as a JsonResult. This worked fine with another AJAX request where I sent only strings. It seems like because FormData is being sent it causes a page refresh, which for me means I never reach the response part of my code. I would appreciate it a lot if someone dug me out of this hole, thanks!
MyPage.cshtml
<input id="readFromFile" type="file"/>
<button class="btn btn-primary" type="submit" onclick="ResultsFromFile()">Get Results</button>
<script>
function ResultsFromFile() {
var temp = $("#readFromFile")[0].files;
if (temp.length > 0) {
if (window.FormData !== undefined) {
var data = new FormData();
for (var x = 0; x < temp.length; x++) {
data.append("file" + x, temp[x]);
}
$.ajax({
type: "POST",
url: 'AlexaIndex?handler=GetResultsFromFile',
headers: { "XSRF-TOKEN": $('input:hidden[name="__RequestVerificationToken"]').val() },
contentType: false,
processData: false,
data: data,
success: function (response) {
console.log('result is ' + response);
var jsonObj = JSON.parse(response);
PopulateTable(jsonObj);
}
});
}
}
</script>
MyPage.cshtml.cs
public JsonResult OnPostGetResultsFromFile()
{
foreach (var file in Request.Form.Files)
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
file.CopyTo(stream);
}
var urlList = ReadFileOfUrls(path);
ModelState.Clear();
var results = _alexaExtractorService.GetAwisResults(urlList);
var json = JsonConvert.SerializeObject(results);
return new JsonResult(json);
}
return new JsonResult("FINITO MINITO");
}
The reason it's happening is because your button is wrapped in a form element, and all button elements with type="submit" will post the form to the server. Prevent the submit button from actually submitting by preventing that default action with e.preventDefault();
<input id="readFromFile" type="file"/>
<button class="btn btn-primary" type="submit" onclick="ResultsFromFile(event)">Get Results</button>
<script>
function ResultsFromFile(e) {
e.preventDefault();
var temp = $("#readFromFile")[0].files;
if (temp.length > 0) {
if (window.FormData !== undefined) {
var data = new FormData();
for (var x = 0; x < temp.length; x++) {
data.append("file" + x, temp[x]);
}
$.ajax({
type: "POST",
url: 'AlexaIndex?handler=GetResultsFromFile',
headers: { "XSRF-TOKEN": $('input:hidden[name="__RequestVerificationToken"]').val() },
contentType: false,
processData: false,
data: data,
success: function (response) {
console.log('result is ' + response);
var jsonObj = JSON.parse(response);
PopulateTable(jsonObj);
}
});
}
}
</script>
Change the button type to type="button" instead of type="submit".
<button class="btn btn-primary" type="button" onclick="ResultsFromFile()">Get Results</button>
This form i want to send using ajax call with file, but it is not calling the c# function and not showing any error.
//form to submit
<form id="formfile" enctype="multipart/form-data">
<div class="modal-body">
<input type="file" id="fileupload1"/>
</div>
<div class="modal-footer">
<input type="submit" id="savefiles" class="buttonType" onclick="saveFile();return false" value="Save File" />
</div>
</form>
This ajax call is used to call c# code and also send file(.pdf)
//ajax call in .aspx file
function saveFile() {
debugger;
var file = $('input[type="file"]').val();
var exts = ['pdf', 'PDF'];
var formData = new FormData();
formData.append("imageFile", $('#fileupload1')[0].files[0]);
if (file) {
var extension = file.substring(file.lastIndexOf('.') + 1, file.length);
if ($.inArray(extension, exts) > -1)
{
//var formData = new FormData($('#form1')[0]);
var fileUpload = $('#fileupload1').get(0);
var files = fileUpload.files;
for (var i = 0; i < files.length; i++) {
formData.append(files[i].name, files[i]);
}
formData.append(fileUpload.name, fileUpload);
//alert('File Uploaded Successfully!');
}
else
{
alert('Invalid file, Only pdf files can be uploaded!!!');
}
}
//var str = "abc";
$.ajax({
url: "FileUploader.aspx/savepdfFiles",
type: "POST",
//cache: false,
contentType: false,
processData: false,
data: formData,
success: function (data) {
debugger;
},
error: function (data) {
debugger
}
});
}
It will come into success section also, but not calling following method.
//c# code
[webMethod]
public static void savepdfFiles()
{
//code
}
In ajax call it goes into success. but not calling savepdfFiles() method.
EDIT 24/08/2016
You can convert your blob data to base 64 and send it in JSON
var filesLength = 0;
function SaveFileToTemp() {
var file = $('input[type="file"]').val();
var exts = ['pdf', 'PDF'];
var pdfList = [];
// var pdfFile = { FileName: '', B64Data: '' };
if (file) {
var extension = file.substring(file.lastIndexOf('.') + 1, file.length);
if ($.inArray(extension, exts) > -1) {
var fileUpload = $('#fileupload1').get(0);
var files = fileUpload.files;
filesLength = files.length;
for (var i = 0; i < files.length; i++) {
var reader = new window.FileReader();
reader.myFileIndex = i;
reader.onloadend = function () {
base64data = reader.result;
pdfList.push({ FileName: files[this.myFileIndex].name, B64Data: base64data.substr(base64data.indexOf(',') + 1) });
console.log(base64data);
filesLength--;
if (filesLength === 0) {
$.ajax({
url: "/FileUploader.aspx/savepdfFiles",
type: "POST",
//cache: false,
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({ listPdf: pdfList }),
success: function (data) {
//alert('File Uploaded Successfully!');
debugger;
},
error: function (data) {
debugger
}
});
}
}
reader.readAsDataURL(files[i]);
}
}
else {
alert('Invalid file, Only pdf files can be uploaded!!!');
}
}
}
and in C#
[WebMethod]
public static void savepdfFiles(List<PdfFile> listPdf)
{
//code
foreach (var item in listPdf)
{
byte[] data = Convert.FromBase64String(item.B64Data);
System.IO.File.WriteAllBytes(string.Format("d:\\temp\\{0}",item.FileName), data) ;
}
}
this is my clas PdfFile for info
public class PdfFile
{
public string FileName { get; set; }
public string B64Data { get; set; }
}
Maybe you have to add this to your web.config to allow big json serialization :
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="10240000"/>
</webServices>
</scripting>
</system.web.extensions>
You have to set the appropiate value for maxJsonLength
Previous answer
I think I had this problem before from what i remember there is mechanism that doesn't allow you to this.
Maybe I'm wrong but I share you a link.
You will have to handle this with an ashx.
here is a built-in validation layer of protection that ASP.NET
enforces for both GET and POST based ASP.NET AJAX web methods, which
is that regardless of the HTTP verb being used, ASP.NET always
requires that the HTTP Content-Type header is set to the value
application/json. It this content type header is not sent, ASP.NET
AJAX will reject the request on the server.
I know this problem and it's easy to solve just you have to use Generic Handler.
Using generic handler with your AJAX call you can send any file to asp.net C# function.
In Generic Handler you have to write following c# code.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
namespace UploadFile
{
/// <summary>
/// Summary description for UploadFileHandler
/// </summary>
public class UploadFileHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//context.Response.ContentType = "text/plain";
//context.Response.Write("Hello World");
if (context.Request.Files.Count > 0)
{
HttpFileCollection files = context.Request.Files;
for (int i = 0; i < files.Count; i++)
{
HttpPostedFile file = files[i];
string fname;
if (HttpContext.Current.Request.Browser.Browser.ToUpper() == "IE" || HttpContext.Current.Request.Browser.Browser.ToUpper() == "INTERNETEXPLORER")
{
string[] testfiles = file.FileName.Split(new char[] { '\\' });
fname = testfiles[testfiles.Length - 1];
}
else
{
fname = file.FileName;
}
fname = Path.Combine(context.Server.MapPath("~/Uploads/"), fname);
file.SaveAs(fname);
}
}
context.Response.ContentType = "text/plain";
context.Response.Write("File Uploaded Successfully!");
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Here is the example...
If you want Full Example Click here!
I'm using Grid.MVC to display data from an entity model. On row click I am getting the value of a cell and passing it to my controller with a json/ajax function.
In my controller the int "ticketnumber" is passing just fine. The thing that I am not understanding is when I hard code the int, it is working (if I directly browse to http://localhost:58779/ticket/PDFVIEW).
The controller seems to be running through just fine, but it is not displaying the PDF..it just takes me back to my grid in my view with the ajax script. Thanks for the help.
Edit - Code:
<script>
$(function () {
pageGrids.TicketGrid.onRowSelect(function (e) {
var ticketnumber = e.row.UnsettledID;
ticketnumber = JSON.stringify({ 'ticketnumber': ticketnumber });
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: '/ticket/PDFVIEW',
data: ticketnumber,
});
});
});
</script>
controller:
[ActionName("PDFVIEW")]
[HttpGet]
public ActionResult PDFVIEW(int ticketnumber)
{
var db = new ScaleTrac_VerticalEntities();
Ticket_UnsettledScaleImages tu = new Ticket_UnsettledScaleImages();
tu = db.Ticket_UnsettledScaleImages.Where(p => p.UnsettledID == ticketnumber).First();
string filename = "ScaleTick" + tu.UnsettledID + ".pdf";
{
byte[] bytes = tu.ScaleTicket;
TempData["bytes"] = bytes;
Response.Clear();
MemoryStream ms = new MemoryStream(bytes);
return new FileStreamResult(ms, "application/pdf");
}
}
You can't use AJAX to download a file in this way. Your AJAX code is getting the contents of the PDF, but your browser needs to receive it as a normal request in order to view it. You should instead render a link to the PdfView action, or use window.setLocation if you need to do it from a Javascript event handler.
Note you'll also need to change your action method to accept HttpGet.
Using what Richard said helped a lot.
My Json I changed to:
<script>
$(function pdfviewer() {
pageGrids.TicketGrid.onRowSelect(function (e) {
var ticketnumber = e.row.UnsettledID;
ticketnumber = JSON.stringify({ 'ticketnumber': ticketnumber });
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: '/ticket/PDFVIEW',
data: ticketnumber,
success: function (d) {
if (d.success) {
window.location = "/Ticket/DownloadFile" + "?fName=" + d.fName;
}
},
error: function () {
alert("Error");
}
});
});
});
</script>
And in my controller I did:
[ActionName("PDFVIEW")]
public ActionResult pdf(int ticketnumber)
{
var db = new ScaleTrac_VerticalEntities();
Ticket_UnsettledScaleImages tu = new Ticket_UnsettledScaleImages();
tu = db.Ticket_UnsettledScaleImages.Where(p => p.UnsettledID == ticketnumber).First();
string filename = "ScaleTick" + tu.UnsettledID + ".pdf";
{
byte[] bytes = tu.ScaleTicket;
TempData["bytes"] = bytes;
Response.Clear();
MemoryStream ms = new MemoryStream(bytes);
var fName = string.Format("File-{0}.pdf", DateTime.Now.ToString("s"));
Session[fName] = ms;
return Json(new { success = true, fName }, JsonRequestBehavior.AllowGet);
}
}
public ActionResult DownloadFile(string fName)
{
var ms = Session[fName] as MemoryStream;
if (ms == null)
return new EmptyResult();
Session[fName] = null;
return File(ms, "application/pdf", fName);
}
Thank you very much!
<body>
<form id="form1" runat="server" enctype="multipart/form-data">
<table id="tblAttachment"> </table>
<input id="btnSubmit" type="button" value="button" />
</form>
</body>
Dynamically inserting a FileUpload Control
<script>
$(document).ready(function () {
var MaxAttachment = 1;
$("#tblAttachment").append('<tr><td><input id=\"Attachment_' + MaxAttachment.toString() + '\" name=\"file\" type=\"file\" /><br><a class=\"MoreAttachment\">Additional Attachment</a></td></tr>');
$("#btnSubmit").on("click", UploadFile);
});
</script>
Sending Data to .ashx using Jquery
function UploadFile() {
var kdata = new FormData();
var i = 0;
//run through each row
$('#tblAttachment tr').each(function (i, row) {
var row = $(row);
var File = row.find('input[name*="file"]');
alert(File.val());
kdata.append('file-' + i.toString(), File);
i = i + 1;
});
sendFile("fUpload.ashx", kdata, function (datad) {
}, function () { alert("Error in Uploading File"); }, true);
}
On .ashx Count always Zero ??
public class fUpload : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
int k = context.Request.Files.Count;
context.Response.Write(UploadMultipleFiles());
}
Ajax Request
function sendFile(requestUrl, dataPayload, successfunc, errorfunc, synchronousMode) {
$.ajax({
url: requestUrl,
type: "POST",
dataType: "json",
contentType: false,
processData: false,
cache: false,
async: synchronousMode,
data: dataPayload,
success: successfunc,
error: errorfunc
});
}
Please Check .. where i m doing wrong
form tag having enctype="multipart/form-data" and each fileUPload control have uniue id and name attribute too
Thanks
You're sending a jQuery object, not a file ?
Shortened down, you're basically doing this
var kdata = new FormData();
var File = row.find('input[name*="file"]'); // jQuery object
kdata.append('file-0', File);
You need the files, not the jQuery objects
var kdata = new FormData();
var File = row.find('input[name*="file"]'); // jQuery object
var files = File.get(0).files[0]; // it not multiple
kdata.append('file-0', Files);