iTextSharp MVC View to PDF - asp.net-mvc

I'm having a little trouble with my TextReader when trying to parse the html string I want to convert to PDF when using iTextSharp.
Function ViewDeliveryNote(ByVal id As Integer) As FileStreamResult
'Memory buffer
Dim ms As MemoryStream = New MemoryStream()
'the document
Dim document As Document = New Document(PageSize.A4)
'the pdf writer
PdfWriter.GetInstance(document, ms)
Dim wc As WebClient = New WebClient
Dim htmlText As String = wc.DownloadString("http://localhost:59800/Warehouse/DeliveryNote/" & id) 'Change to live URL
Dim worker As html.simpleparser.HTMLWorker = New html.simpleparser.HTMLWorker(document)
Dim reader As TextReader = New StringReader(htmlText)
document.Open()
worker.Open()
worker.StartDocument()
worker.Parse(reader)
worker.EndDocument()
worker.Close()
document.Close()
'ready the file stream
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition", "attachment;filename=DeliveryNote.pdf")
Response.Buffer = True
Response.Clear()
Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer.Length)
Response.OutputStream.Flush()
Response.End()
Return New FileStreamResult(Response.OutputStream, "application/pdf")
End Function
The line it stops on is worker.Parse(reader) with the error Object reference not set to an instance of an object even though StringReader(htmlText) has successfully read the HTML page.
I'm not sure what I'm doing wrong or what I'm missing at the moment so I would be grateful for any assistance.
UPDATE I just tried Dim reader As New StringReader(htmlText) instead but to no avail. Although htmlText still definitely contains a value, but the object thinks that it doesn't.

I would definitely write a custom action result for this to avoid polluting my controller. Also all those undisposed disposable resources in your code should be taken care of:
Public Class PdfResult
Inherits ActionResult
Private ReadOnly _id As Integer
Public Sub New(ByVal id As Integer)
_id = id
End Sub
Public Overrides Sub ExecuteResult(context As ControllerContext)
If context Is Nothing Then
Throw New ArgumentNullException("context")
End If
Dim response = context.HttpContext.Response
response.Buffer = True
response.ContentType = "application/pdf"
response.AddHeader("Content-Disposition", "attachment; filename=DeliveryNote.pdf")
Using client = New WebClient()
Dim htmlText As String = client.DownloadString("http://localhost:59800/Warehouse/DeliveryNote/" & _id) 'Change to live URL
Dim doc = New Document(PageSize.A4)
PdfWriter.GetInstance(doc, response.OutputStream)
Dim worker = New HTMLWorker(doc)
doc.Open()
worker.Open()
Using reader = New StringReader(htmlText)
worker.Parse(reader)
End Using
doc.Close()
End Using
End Sub
End Class
and then simply:
Function ViewDeliveryNote(ByVal id As Integer) As ActionResult
Return New PdfResult(id)
End Function
You should also make sure that the server has access to the desired url. Don't forget that his request will execute in the context of the Network Account which might not have the same privileges as normal accounts.

Related

How can i get a Id parameter from URL in vb.net MVC?

I'm writing code to perform CRUD operations using JavaScript and VB.NET MVC. I'll require a method to send parameters from Controller to the View, but I don't know how to catch the value from the URL.
Following is my controller:
' GET: Bodega/Index/5
Function Edit(ByVal id As Integer, ByVal collection As FormCollection) As ActionResult
Try
Dim constr As String = ConfigurationManager.ConnectionStrings("conexion").ConnectionString
Using con As New SqlConnection(constr)
Using cmd As New SqlCommand("SELECT * FROM bodega WHERE id = #Id")
cmd.Parameters.AddWithValue("#Id", id)
cmd.Connection = con
Dim data As New List(Of Bodega)()
con.Open()
Using sdr As SqlDataReader = cmd.ExecuteReader()
While sdr.Read()
data.Add(New Bodega() With {
.id = Convert.ToInt32(sdr("id")),
.nombre = sdr("nombre").ToString(),
.ubicacion = sdr("ubicacion").ToString(),
.lugar = sdr("lugar").ToString(),
.lat = sdr("lat").ToString(),
.lng = sdr("lng").ToString()
})
End While
End Using
con.Close()
Return View("Index", id)
End Using
End Using
Catch e As Exception
Return RedirectToAction("Index")
End Try
End Function
I'm sending the id to the View via the return statement, but in the view i don't know what command can use.
The URL format is:
localhost/Bodega/Edit/5
I need catch the ID 5. If as possible save into a hidden input or send to a javascript code to load the data pre-load for the inputs.
In the Edit view that is calling the Edit action, make sure that the route value is called id so that it can bind to the id parameter. Also, it doesn’t seem like you’re using the FormCollection parameter. And did you mean to pass the new Bodega instance you’re creating over to the view instead of the id? I’m guessing that you are looking up a bodega to be edited in an edit view?

How to save any file/data in application folder in MVC?

I'm using a library for converting HTML to PDF. After converting to PDF how can I save this converted PDF file in application folder in a controller?
Here is the code:
public ActionResult ABC(ResearchProposal model)
{
ViewDataDictionary viewData = new ViewDataDictionary(model);
// transmit the posted data to view
viewData["MyModel"] = model;
StringWriter stringWriter = new StringWriter();
// Render the Index view in a HTML string
ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, "ABC", null);
ViewContext viewContext = new ViewContext(
ControllerContext,
viewResult.View,
viewData,
new TempDataDictionary(),
stringWriter
);
viewResult.View.Render(viewContext, stringWriter);
// Get the view HTML string
string htmlToConvert = stringWriter.ToString();
// Get the base URL
String currentPageUrl = this.ControllerContext.HttpContext.Request.Url.AbsoluteUri;
String baseUrl = currentPageUrl.Substring(0, currentPageUrl.Length - "Reports/ABC".Length);
// Create a HTML to PDF converter object with default settings
HtmlToPdfConverter htmlToPdfConverter = new HtmlToPdfConverter();
// Set license key received after purchase to use the converter in licensed mode
// Leave it not set to use the converter in demo mode
htmlToPdfConverter.LicenseKey = "fvDh8eDx4fHg4P/h8eLg/+Dj/+jo6Og=";
// Set an adddional delay in seconds to wait for JavaScript or AJAX calls after page load completed
// Set this property to 0 if you don't need to wait for such asynchcronous operations to finish
htmlToPdfConverter.ConversionDelay = 2;
// Convert the HTML string to a PDF document in a memory buffer
byte[] outPdfBuffer = htmlToPdfConverter.ConvertHtml(htmlToConvert, baseUrl);
// Send the PDF file to browser
FileResult fileResult = new FileContentResult(outPdfBuffer, "application/pdf");
fileResult.FileDownloadName = "Convert_Current_Page.pdf";
return fileresult;
}
You can use File.WriteAllBytes to save the bytes of the FileContentResult that you got.
You will need to map the relative path of the server for this to work, as relative path only works in the context of ASP.NET.
string filename = "Convert_Current_Page.pdf";
string path = Server.MapPath("~G/Initial try/Content/data/");
path = Path.Combine(path, filename);
File.WriteAllBytes(path, fileResult.FilecContents);

How can I post JSON to an ASP.NET controller action?

How can I post JSON to an ASP.NET MVC controller action?
sample scenario
An external authentication system requires JSON be posted to it. I would like to capture the user's credentials on my site and forward them as JSON to the authentication site. Redirects are not an option.
So we'll use http://www.example.com/ExtAuth/Login as our fictitious external authentication endpoint.
ExtAuth will expect us to post a JSON string representing an object with two properties: User, and Password.
ExtAuth will return a JSON string representing an object with two properties: Status, and Message.
The key to making this whole thing work is the extRequest.ContentType. It MUST be set to application/json.
I'll leave proper error handling as an exercise to the user.
<HttpPost>
<AllowAnonymous>
Public Function Login(ByVal model As LoginModel) As ActionResult
Dim authEndpointUrl As String = "http://www.example.com/ExtAuth/Login"
Dim result As String = String.Empty ' this will hold the JSON returned from ExtAuth
Dim resultModel As LoginResult = Nothing ' The deserialized form of the result JSON
Dim data As String = String.Empty ' the serialized representation of our login data
Dim extRequest As HttpWebRequest = WebRequest.CreateHttp(authEndpointUrl)
extRequest.Method = "POST"
extRequest.ContentType = "application/json"
data = Newtonsoft.Json.JsonConvert.SerializeObject(model)
Using writer As StreamWriter = New StreamWriter(extRequest.GetRequestStream)
writer.Write(data)
End Using
Using extResponse As HttpWebResponse = extRequest.GetResponse
Using reader As StreamReader = New StreamReader(extResponse.GetResponseStream)
result = reader.ReadToEnd
End Using
End Using
resultModel = Newtonsoft.Json.JsonConvert.DeserializeObject(Of LoginResult)(result)
ViewData("Status") = resultModel.Status
ViewData("Message") = resultModel.Message
Return View(model)
End Function
This method should work for MVC 3+.

ASP.NET MVC FileStreamResult, fileDownloadName is not used

The following returns a PDF which the browser tries to directly display inline. This works correctly. However, if I try to download the file, the download name is not "myPDF.pdf", but instead the ID in the route (myapp/controller/PDFGenerator/ID). Is it possible to set the file download name to be "myPDF.pdf"?
public FileStreamResult PDFGenerator(int id)
{
MemoryStream ms = GeneratePDF(id);
byte[] file = ms.ToArray();
MemoryStream output = new MemoryStream();
output.Write(file, 0, file.Length);
output.Position = 0;
HttpContext.Response.AddHeader("content-disposition",
"inline; filename=myPDF.pdf");
return File(output, "application/pdf", fileDownloadName="myPDF.pdf");
}
No, this is not possible with a PDF displayed inline. You could achieve this if you send the Content-Disposition header with as an attachment:
public ActionResult PDFGenerator(int id)
{
Stream stream = GeneratePDF(id);
return File(stream, "application/pdf", "myPDF.pdf");
}
Also notice how I removed the unnecessary MemoryStream you were using and loading the PDF in memory where you could have directly streamed it to the client which would have been far more efficient.
If you are using FileStreamResult to download the file, try using this in controller
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=FileName.pdf");
It is possible by making the id a string which represents the file name without the extension.
public ActionResult PDFGenerator(string id, int? docid)
{
Stream stream = GeneratePDF(docid);
return new FileStreamResult(stream , "application/pdf");
}
The url then then end like this
..PDFGenerator/Document2?docid=15

How can I present a file for download from an MVC controller?

In WebForms, I would normally have code like this to let the browser present a "Download File" popup with an arbitrary file type, like a PDF, and a filename:
Response.Clear()
Response.ClearHeaders()
''# Send the file to the output stream
Response.Buffer = True
Response.AddHeader("Content-Length", pdfData.Length.ToString())
Response.AddHeader("Content-Disposition", "attachment; filename= " & Server.HtmlEncode(filename))
''# Set the output stream to the correct content type (PDF).
Response.ContentType = "application/pdf"
''# Output the file
Response.BinaryWrite(pdfData)
''# Flushing the Response to display the serialized data
''# to the client browser.
Response.Flush()
Response.End()
How do I accomplish the same task in ASP.NET MVC?
Return a FileResult or FileStreamResult from your action, depending on whether the file exists or you create it on the fly.
public ActionResult GetPdf(string filename)
{
return File(filename, "application/pdf", Server.UrlEncode(filename));
}
To force the download of a PDF file, instead of being handled by the browser's PDF plugin:
public ActionResult DownloadPDF()
{
return File("~/Content/MyFile.pdf", "application/pdf", "MyRenamedFile.pdf");
}
If you want to let the browser handle by its default behavior (plugin or download), just send two parameters.
public ActionResult DownloadPDF()
{
return File("~/Content/MyFile.pdf", "application/pdf");
}
You'll need to use the third parameter to specify a name for the file on the browser dialog.
UPDATE: Charlino is right, when passing the third parameter (download filename) Content-Disposition: attachment; gets added to the Http Response Header. My solution was to send application\force-download as the mime-type, but this generates a problem with the filename of the download so the third parameter is required to send a good filename, therefore eliminating the need to force a download.
You can do the same in Razor or in the Controller, like so..
#{
//do this on the top most of your View, immediately after `using` statement
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
}
Or in the Controller..
public ActionResult Receipt() {
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
return View();
}
I tried this in Chrome and IE9, both is downloading the pdf file.
I probably should add I am using RazorPDF to generate my PDFs. Here is a blog about it: http://nyveldt.com/blog/post/Introducing-RazorPDF
You should look at the File method of the Controller. This is exactly what it's for. It returns a FilePathResult instead of an ActionResult.
mgnoonan,
You can do this to return a FileStream:
/// <summary>
/// Creates a new Excel spreadsheet based on a template using the NPOI library.
/// The template is changed in memory and a copy of it is sent to
/// the user computer through a file stream.
/// </summary>
/// <returns>Excel report</returns>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NPOICreate()
{
try
{
// Opening the Excel template...
FileStream fs =
new FileStream(Server.MapPath(#"\Content\NPOITemplate.xls"), FileMode.Open, FileAccess.Read);
// Getting the complete workbook...
HSSFWorkbook templateWorkbook = new HSSFWorkbook(fs, true);
// Getting the worksheet by its name...
HSSFSheet sheet = templateWorkbook.GetSheet("Sheet1");
// Getting the row... 0 is the first row.
HSSFRow dataRow = sheet.GetRow(4);
// Setting the value 77 at row 5 column 1
dataRow.GetCell(0).SetCellValue(77);
// Forcing formula recalculation...
sheet.ForceFormulaRecalculation = true;
MemoryStream ms = new MemoryStream();
// Writing the workbook content to the FileStream...
templateWorkbook.Write(ms);
TempData["Message"] = "Excel report created successfully!";
// Sending the server processed data back to the user computer...
return File(ms.ToArray(), "application/vnd.ms-excel", "NPOINewFile.xls");
}
catch(Exception ex)
{
TempData["Message"] = "Oops! Something went wrong.";
return RedirectToAction("NPOI");
}
}
Although standard action results FileContentResult or FileStreamResult may be used for downloading files, for reusability, creating a custom action result might be the best solution.
As an example let's create a custom action result for exporting data to Excel files on the fly for download.
ExcelResult class inherits abstract ActionResult class and overrides the ExecuteResult method.
We are using FastMember package for creating DataTable from IEnumerable object and ClosedXML package for creating Excel file from the DataTable.
public class ExcelResult<T> : ActionResult
{
private DataTable dataTable;
private string fileName;
public ExcelResult(IEnumerable<T> data, string filename, string[] columns)
{
this.dataTable = new DataTable();
using (var reader = ObjectReader.Create(data, columns))
{
dataTable.Load(reader);
}
this.fileName = filename;
}
public override void ExecuteResult(ControllerContext context)
{
if (context != null)
{
var response = context.HttpContext.Response;
response.Clear();
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.AddHeader("content-disposition", string.Format(#"attachment;filename=""{0}""", fileName));
using (XLWorkbook wb = new XLWorkbook())
{
wb.Worksheets.Add(dataTable, "Sheet1");
using (MemoryStream stream = new MemoryStream())
{
wb.SaveAs(stream);
response.BinaryWrite(stream.ToArray());
}
}
}
}
}
In the Controller use the custom ExcelResult action result as follows
[HttpGet]
public async Task<ExcelResult<MyViewModel>> ExportToExcel()
{
var model = new Models.MyDataModel();
var items = await model.GetItems();
string[] columns = new string[] { "Column1", "Column2", "Column3" };
string filename = "mydata.xlsx";
return new ExcelResult<MyViewModel>(items, filename, columns);
}
Since we are downloading the file using HttpGet, create an empty View without model and empty layout.
Blog post about custom action result for downloading files that are created on the fly:
https://acanozturk.blogspot.com/2019/03/custom-actionresult-for-files-in-aspnet.html
Use .ashx file type and use the same code

Resources