C# MVC HTML to EXCEL not working - c#

I had a controller method to convert html to excel which worked fine when it was not a web method. But stopped working when the html string got too large. I converted it to a web method which I call via jQuery. It passes the data to the method, but no longer creates the Excel file. The code within the method has not changed. The only difference is the way I call it and the [HttpPost] decoration. The code follows:
[HttpPost]
[ValidateInput(false)]
public void ExportExcel(string aContent)
{
StringBuilder sb = new StringBuilder();
sb.Append(aContent);
Response.ClearContent();
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", "attachment; filename=ProjectData.xls");
Response.Write(sb);
Response.End();
}
And this is the jQuery stuff:
// namespace
var excel = excel || {};
$(document).ready(function () {
$('#ExcelUrl').on('click', function () {
var table = $('#tableName').val();
var content = $('#' + table).html();
excel.utilFunctions.exportToExcel(content);
});
});
excel.utilFunctions = (function ($) {
var exportToExcel = function (content) {
$.ajax({
url: "/Home/ExportExcel",
type: "POST",
data: { aContent: content }
}).done(function(data) {
alert(data);
}).fail(function(error) {
alert(error.responseText);
});
};
//public functions
return {
exportToExcel: exportToExcel
};
})(jQuery);
Any help is appreciated.

Found a solution. Apparently, since jQuery is doing the post, a file will never be generated no matter what you do.
I changed the jQuery function to create and submit a hidden form field that has my data in it.
$(document).ready(function () {
$('#ExcelUrl').on('click', function () {
var table = $('#tableName').val();
var content = $('#' + table).html().replace(/\s/g, "");
excel.utilFunctions.exportToExcel(content);
});
});
excel.utilFunctions = (function ($) {
// Excel export function
var exportToExcel = function (content) {
var form = $("<form></form>");
form.attr("method", "POST");
form.attr("action", "/Home/ExportExcel");
var dataField = $("<input/>");
dataField.attr("type", "hidden");
dataField.attr("name", "aContent");
dataField.attr("value", content);
form.append(dataField);
$(document.body).append(form);
form.submit();
};
//public functions
return {
exportToExcel: exportToExcel
};
})(jQuery);
Then I changed the ExportExcel controller function to return a FileStreamResult
public FileStreamResult ExportExcel(string aContent)
and modified the code that does the actual exporting
var byteArray = Encoding.ASCII.GetBytes(aContent);
var stream = new MemoryStream(byteArray);
return File(stream, "application/ms-excel", "FileName.xls");

Related

Ajax call to async method returning file successfully but success/complete part of an Ajax request is not getting executed

I am trying to export selected records in to a file and reload the page to update the records in a current view. I am calling web api asynchronously to get all the records. An AJAX call is executing an action in a controller successfully and returning expected data without any error but none of the 'success', 'complete' or 'error' part of ajax function is executing. There are no errors in a developer tool of the browser, no exception, nothing unusual so its getting trickier for me to investigate this issue further. Can I request your a suggestions on this please? Thanks
View :
#Html.ActionLink("Export records", "Index", null, new { Id = "myExportLinkId")
Script :
$("a#myExportLinkId").click(function (e) {
var selected = "";
$('input#myCheckBoxList').each(function () {
if (this.checked == true) {
selected += $(this).val() + ',';
}
});
if (selected != "") {
$.ajax({
url: '/MyController/MyAction',
type: 'GET',
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
'MyString': 'stringValue'
},
success: function (data) {
alert("success");
},
error: function () {
alert("error");
}
});
})
And the action/method looks like this :
[HttpGet]
public async Task<ActionResult> ExportNewOrders(string OrderIdString)
{
//code to create and store file
//actually want to send the file details as json/jsonResult but for testing only returning
//string here
return Json( "Success", "application/json", JsonRequestBehavior.AllowGet);
}
Finally I have resolved this with Promisify functionality of an AJAX call. Obviously the json response I was returning had an issue so I have replaced
return Json( "Success", "application/json", JsonRequestBehavior.AllowGet);
to
return new JsonResult(){
Data = new { success = true, guid = handle, fileName = exportFileName },
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
which has fixed the bug and the success function of ajax call got executed.
But other than this there were issues to wait until the file download (which involved encryption decryption, server validations etc) completes and then refresh the page. This I have resolved by implementing an ajax call with Promisify fuctionality. You can find codepen example here and the original post here.
Here is the complete code.
View/HTML
#Html.ActionLink("Export", "yourActionName", null, new { Id = "exportRequest", #onclick = "letMeKnowMyFileIsDownloaded();" })
Script/Ajax
function letMeKnowMyFileIsDownloaded() {
return new Promise(function (resolve, reject) {
$("a#exportRequest").on("click", function () {
$.ajax({
url: this.href + "?param=whatever params you want to pass",
dataType: "json",
data: {
'param1': 'value'
},
success: function (data) {
var a = document.createElement("a");
var url = '/yourControllerName/Download?fileGuid=' + data.guid + '&filename=' + data.fileName;//window.URL.createObjectURL(data);
a.href = url;
a.download = data.fileName;
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
resolve(true);
},
error: function (error) {
reject(error);
}
});
});
});
}
letMeKnowMyFileIsDownloaded()
.then(function (bool) {
if (bool) {
//alert("File downloaded 👇");
window.location.reload(1);
}
})
.catch(function (error) {
alert("error");
});
I have used nuget package ClosedXML to handle excel file functionality. Using the stream to create and download the data in excel file without storing the file physically on the server.
And in the controller
//can be async or sync action
public async Task<ActionResult> Index(YourModel model)
{
//do stuff you want
var exportOrders = your_object;
//using DataTable as datasource
var dataSource = new DataTable();
//write your own function to convert your_object to your_dataSource_type
dataSource = FormatTypeToDataTable(exportOrders);
if (dataSource != null && dataSource.Rows.Count > 0)
{
//install ClosedXML.Excel from nuget
using (XLWorkbook wb = new XLWorkbook())
{
try
{
var handle = Guid.NewGuid().ToString();
wb.Worksheets.Add(dataSource, "anyNameForSheet");
string exportFileName = "yourFileName" + ".xlsx";
MemoryStream stream = GetStream(wb);
TempData[handle] = stream; exportFileName);
return new JsonResult()
{
Data = new { success = true, guid = handle, fileName = exportFileName },
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
catch (Exception ex)
{
//ModelState.AddModelError("", ex.Message);
}
}
}
}
public virtual ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
var stream = TempData[fileGuid] as MemoryStream;
var data = stream.ToArray();
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
return new EmptyResult();
}
}

C# Razor WebAPI Upload File with AJAX 400 Error

I'm trying to make use of the answer provided here:
Upload File Using WebAPI Ajax
But I keep receiving a 400 (Bad Request) error.
I've been submitting a pdf file but I keep receiving this error...
What am I doing wrong?
(FYI I'm not using MVC)
My code:
CSHTML (using Razor Syntax)
#{
Layout = "~/_SiteLayout.cshtml";
}
<label>Enter File</label>
<input type="file" name="UploadFile" id="datasheet_uploadfile" class="" accept="application/pdf"/>
<script>
$(document).ready(function() {
$('#datasheet_uploadfile').change(function() {
var data = new FormData();
var file = this.files;
data.append('file', file);
$.ajax({
url: '/api/file',
processData: false,
contentType: false,
data: data,
type: 'POST'
}).done(function(result) {
alert(result);
}).fail(function(a, b, c) {
console.log(a, b, c);
});
});
});
</script>
My WebAPI Controller
FileController.cs
public class FileController : ApiController
{
// POST api/<controller>
public HttpResponseMessage Post()
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
var docfiles = new List<string>();
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
int hasheddate = DateTime.Now.GetHashCode();
//Good to use an updated name always, since many can use the same file name to upload.
string changed_name = hasheddate.ToString() + "_" + postedFile.FileName;
var filePath = HttpContext.Current.Server.MapPath("~/Content/stuff/" + changed_name);
postedFile.SaveAs(filePath); // save the file to a folder "Images" in the root of your app
changed_name = #"~\Content\stuff\" + changed_name; //store this complete path to database
docfiles.Add(changed_name);
}
result = Request.CreateResponse(HttpStatusCode.Created, docfiles);
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
}
Use the below code to upload files
$(document).ready(function () {
$('#datasheet_uploadfile').change(function () {
var data = new FormData();
data.append("file", this.files[0]);

Adding image to AngularJS model for upload

I am new to this wonderful framework AngularJS. I have a C# API controller where I would like to upload data from a form that includes an image. Normally (razor) I would upload a form as json and include the image as a HttpPostedFileBase:
public ArtWork SaveArtWork(ArtWork artWork, HttpPostedFileBase file)
{ // save in db and return object }
I have found a lot of different ways for uploading the file wrapped in a FormData object ([AngularJS Uploading An Image With ng-upload):
$scope.uploadFile = function(files) {
var fd = new FormData();
//Take the first selected file
fd.append("file", files[0]);
$http.post(uploadUrl, fd, {
withCredentials: true,
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).success( ...all right!... ).error( ..damn!... );
};
But I have some other properties I have parsed to a json object, and now I would like to upload it all in a bundle. Or is it possible to get the image data as a base64 and add it to my json object? I know that a base64 is 1/3 bigger than a byte stream, but it's so easy to work with :)
Here's my Angular Controller:
'use strict';
(function () {
// Factory
angular.module('umbraco').factory('artworkResource', function ($http) {
return {
getById: function (id) {
return $http.get("backoffice/Trapholt/ArtWorkApi/GetById/" + id);
},
save: function (artwork) {
return $http.post("backoffice/Trapholt/ArtWorkApi/SaveArtWork", angular.toJson(artwork));
},
save2: function (artwork, fd) {
return $http.post("backoffice/Trapholt/ArtWorkApi/SaveArtWork", angular.toJson(artwork), fd);
}
};
});
// Controller
function artworkController($scope, $routeParams, artworkResource, $http) {
$scope.categories = ['Keramik', 'Maleri', 'Møbel', 'Skulptur'];
artworkResource.getById($routeParams.id).then(function (response) {
$scope.curatorSubject = response.data;
});
var fd;
$scope.uploadFile = function(files) {
fd = new FormData();
fd.append("file", files[0]);
};
$scope.save = function (artwork) {
artworkResource.save(artwork, fd).then(function (response) {
$scope.artwork = response.data;
alert("Success", artwork.Title + " er gemt");
});
};
};
//register the controller
angular.module("umbraco").controller('ArtworkTree.EditController', artworkController);
})();
So how can I combine my image and the other properties in one json object or two arguments? Please leave a comment if I need to explain some more, any help would really be appreciated :)
I found a solution, where I added the file and the model to the form data. So it was actually pretty easy to expand solution from here. This is my Angular controller:
function artworkController($scope, $routeParams, artworkResource, $http) {
$scope.categories = ['Keramik', 'Maleri', 'Møbel', 'Skulptur'];
artworkResource.getById($routeParams.id).then(function (response) {
$scope.curatorSubject = response.data;
});
var fd;
$scope.uploadFile = function(files) {
fd = new FormData();
fd.append("file", files[0]);
};
$scope.save = function (artwork) {
fd.append("ArtWork", angular.toJson(artwork));
$http.post("backoffice/Trapholt/ArtWorkApi/Post", fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
});
};
};
And this i my C# mvc API controller:
public HttpResponseMessage Post()
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
var file = httpRequest.Files[0];
var artworkjson = httpRequest.Form[0];
var artwork = JsonConvert.DeserializeObject<ArtWork>(artworkjson);
if (artwork == null)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "No saved");
}
using (var binaryReader = new BinaryReader(file.InputStream))
{
artwork.Picture = binaryReader.ReadBytes(file.ContentLength);
}
result = Request.CreateResponse(HttpStatusCode.Created, "ok");
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
The html view is a normal form where all the inputs are bound with the model, expect the file input field:
<input type="file" id="file" name="picture" onchange="angular.element(this).scope().uploadFile(this.files)"/>

download data as .xls format file

I need to download list data as .xls file.My controller code is follows.
[HttpGet]
public void AttendeeListToExport()
{
string campaign_id = string.Empty;
campaign_id = ((MemberProfile)HttpContext.Current.Profile).HOWCampaignID;
AutoCRM.Services.HOW.Attendee.Manage manage = new AutoCRM.Services.HOW.Attendee.Manage();
DataSet lst = manage.AttendeeListToExport(campaign_id);
if (lst != null)
{
if (lst.Tables[0].Rows.Count > 0)
{
DataTable dt = lst.Tables[0];
// Export all the details to Excel
string filename = campaign_id + "_" + DateTime.Now.ToString("ddMMyyyy") + ".xls";
Export objExport = new Export();
objExport.ExportDetails(dt, Export.ExportFormat.Excel, filename);
}
}
}
js code
$('#exportToExcel').on("click", function () {
alert('hi');
$.ajax({
url: "/api/Attendee/AttendeeListToExport",
async: true,
cache: false,
success: function (result) {
alert(result);
}
});
});
code executing correctly but file not downloading
You can download file through javascript in following both ways :
Using HiddenIFrame :
var downloadURL = function downloadURL(url) {
var iframe;
var hiddenIFrameID = 'hiddenDownloader';
iframe = document.getElementById(hiddenIFrameID);
if (iframe === null) {
iframe = document.createElement('iframe');
iframe.id = hiddenIFrameID;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
iframe.src = url;
}
Using Jquery :
$('a').click(function(e) {
e.preventDefault(); //stop the browser from following
window.location.href = 'uploads/file.doc';
});
Download now!
If your Controller action returned a FileResult, you could download this from your javascript function like this:
$('#exportToExcel').on("click", function () {
window.open("/api/Attendee/AttendeeListToExport", "_blank");
});
You can't download files using ajax. You have to use a hidden iframe or link directly to the file.

Create a query suggestion page based on keywords

I have an asp.net page and am writing some words in a question textbox named txtTitle. After moving to another txtbox named txtbox2, I want to open a question-suggestion page based on the keyword typed in txtbox1 in the space before txtbox2. I've tried this but it is not working. Can anyone help?
<script type="text/javascript">
$(function() {
$("#txtTitle").blur(function() { QuestionSuggestions(); });
});
function QuestionSuggestions() {
var s = $("#txtTitle").val();
if (s.length > 2) {
document.title = s + " - ABC";
$("#questionsuggestions").load("QuestionList.aspx?title=" + escape(s));
}
}
</script>
Create a WebService WebMethod in ASP.Net/C# that loads the suggestions. Your code in the WebMethod will load the datatable with the suggestions. In your WebMethod, return the DataTable as a hashtable, and it will automatically be converted to a JSON object.
public Suggestions<Hashtable> GetSuggestion()
{
DataTable dt = new DataTable();
List<Hashtable> ht = new List<Hashtable>();
string q = Context.Request.QueryString["Q"];
dt = [code to load datatable]
ht = MethodToConvertToHashTable(dt);
dt.Dispose();
return ht;
}
In your txtTitle.blur function, call question suggestions, which does a jQuery AJAX call to the webmethod (I left out the obvious).
function QuestionSuggestions() {
var s = $("#txtTitle").val();
var myJSON = $.ajax({
url: '/directory/svc/yourwebservicepage.asmx/GetSuggestion?Q=' + s,
type: 'POST',
contentType: 'application/json',
success: function (msg) {
var d = msg.d; //THIS IS YOUR JSON OBJECT
}
}
}

Categories

Resources