How to write PDF to HttpResponseMessage using iText 7 - c#

I'm trying to generate a PDF and write it to HTTP response using iText 7 and iText7.pdfHtml libraries.
The HTML content for the PDF is stored in a StringBuilder object.
Not sure what the correct process of doing this is because as soon as I use HtmlConverter.ConvertToPdf, the MemoryStream is closed and I cannot access the bytes. I get the following exception:
System.ObjectDisposedException: Cannot access a closed Stream.
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
StringBuilder htmlText = new StringBuilder();
htmlText.Append("<html><body><h1>Hello World!</h1></body></html>");
using (MemoryStream memoryStream = new MemoryStream())
{
using (PdfWriter pdfWriter = new PdfWriter(memoryStream))
{
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument);
string headerText = "my header";
string footerText = "my footer";
pdfDocument.AddEventHandler(PdfDocumentEvent.END_PAGE, new HeaderFooterEventHandler(document, headerText, footerText));
HtmlConverter.ConvertToPdf(htmlText.ToString(), pdfWriter);
memoryStream.Flush();
memoryStream.Seek(0, SeekOrigin.Begin);
byte[] bytes = new byte[memoryStream.Length];
memoryStream.Read(bytes, 0, (int)memoryStream.Length);
Stream stream = new MemoryStream(bytes);
httpResponseMessage.Content = new StreamContent(stream);
httpResponseMessage.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/pdf");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "sample.pdf"
};
httpResponseMessage.StatusCode = HttpStatusCode.OK;
}//end using pdfwriter
}//end using memory stream
EDIT
Added PdfDocument and Document objects to manipulate header/footer and new page.

Make use of the fact that you have a MemoryStream and replace
memoryStream.Flush();
memoryStream.Seek(0, SeekOrigin.Begin);
byte[] bytes = new byte[memoryStream.Length];
memoryStream.Read(bytes, 0, (int)memoryStream.Length);
by
byte[] bytes = memoryStream.ToArray();
That method is documented to also work with closed memory streams.

Related

How can i show a pdf on webbrowser using byte Array. C# [duplicate]

This question already has answers here:
How to return PDF to browser in MVC?
(11 answers)
Closed 8 months ago.
im trying to create a pdf file and im trying to return this file on imsonia or webbrowser, but i dont how to do this.
here is my code:
string beneficiarioRelatorio = "Test"
var stream = new MemoryStream();
PdfWriter writer = new PdfWriter(stream);
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf);
//Paragraph header = new Paragraph(beneficiarioRelatorio);
document.Add(new Paragraph(beneficiarioRelatorio));
document.Close();
//return stream.ToArray();
//System.IO.File.WriteAllBytes("hello.pdf", stream.ToArray());
var teste = new FileStream(stream, FileMode.Open);
return new FileStreamResult(teste,"application/pdf");
try it
[HttpGet(Name = "GetPDF")]
public ActionResult GetPDF()
{
string beneficiarioRelatorio = "Test";
MemoryStream ms = new MemoryStream();
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, ms);
doc.Open();
doc.Add(new Paragraph(beneficiarioRelatorio));
doc.Close();
byte[] bytes = ms.ToArray();
return File(bytes, "application/pdf");
}

Cannot access a closed Stream. When using PDFReader

I have this file, which is a Stream:
var streamFile = await graphClient.Me.Drive.Items["id"].Content.Request().GetAsync();
Now I am trying to use PdfReader and PdfStamper to set Fields like so:
MemoryStream outFile = new MemoryStream();
PdfReader pdfReader = new PdfReader(streamFile);
PdfStamper pdfStamper = new PdfStamper(pdfReader, outFile);
AcroFields fields = pdfStamper.AcroFields;
fields.SetField("Full_Names", "JIMMMMMMAYYYYY");
pdfStamper.Close();
pdfReader.Close();
But when I try to do this, I get this error:
Cannot access a closed Stream.
On this line:
pdfReader.Close();
What am I doing wrong?
UPDATE
I tried this, still getting the same error:
using (MemoryStream outFile = new MemoryStream())
{
var streamFile = await graphClient.Me.Drive.Items["item-id"].Content.Request().GetAsync();
using (PdfReader pdfReader = new PdfReader(streamFile))
{
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, outFile))
{
AcroFields fields = pdfStamper.AcroFields;
fields.SetField("Full_Names", "JIMMMMMMAYYYYY");
}
}
outFile.Position = 0;
await graphClient.Me.Drive.Items["item-id"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(outFile);
}
UPDATE
I have tried converting the Stream to bytes like so:
var streamFile = await graphClient.Me.Drive.Items["item-id"].Content.Request().GetAsync();
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = streamFile.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
using (PdfReader pdfReader = new PdfReader(ms.ToArray()))
{
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, ms))
{
AcroFields fields = pdfStamper.AcroFields;
fields.SetField("Full_Names", "JIMMMMMMAYYYYY");
}
}
await graphClient.Me.Drive.Items["item-id"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(ms);
}
Same result...Cannot access a closed Stream on this line:
await graphClient.Me.Drive.Items["item-id"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(ms);
The PutAsync is expecting a Stream as well
So when I do this:
var streamFile = await graphClient.Me.Drive.Items["item-id"].Content.Request().GetAsync();
await graphClient.Me.Drive.Items["item-id"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(streamFile);
It uploads the file no problem. So I do believe the problem is trying to edit the PDF with iTextSharp.
In my case I wanted to create the document in memory and add WaterMark after creation of document, but without saving a physical file as an intermediate step (which works as well but not nearly as neat).
public byte[] AsArray(List<DocumentData> list)
{
MemoryStream streamIn = new MemoryStream(); // Set the initial stream for the document
MemoryStream streamOut = new MemoryStream(); // Set the result output stream
PdfWriter writer = new PdfWriter(streamIn); // create the writer for document
CreateDocument(writer, list); // Method where the document actually get's made
// Now the tricky bit
// Translate the `streamIn` (that now contains the document stream) into a PdfReader
// use the byte[] from streamIn to create a new MemoryStream()
PdfReader reader = new PdfReader(new MemoryStream(streamIn.ToArray()));
writer = new PdfWriter(streamOut); // Set the writer stream to be the streamOut
SetWaterMark(reader, writer); // Method to read through the document and add watermarks
return streamOut.ToArray();
}
and hey Presto, succes !
You can try the following:
var streamFile = await graphClient.Me.Drive.Items["item-id"].Content.Request().GetAsync();
byte[] buffer = new byte[16 * 1024];
try
{
PdfReader pdfReader = null;
PdfStamper pdfStamper = null;
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = streamFile.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
pdfReader = new PdfReader(ms.ToArray());
pdfStamper = new PdfStamper(pdfReader, ms);
AcroFields fields = pdfStamper.AcroFields;
fields.SetField("Full_Names", "JIMMMMMMAYYYYY");
await graphClient.Me.Drive.Items["item-id"].ItemWithPath("NewDocument-2.pdf").Content.Request().PutAsync<DriveItem>(ms);
}
}
finally
{
if (pdfReader != null) pdfReader.Dispose();
if (pdfStamper != null) pdfStamper.Dispose();
}
Dispose the pdfReader and pdfStamper after the await is done.

iText 7 return Pdf from Asp.Net WebApi

So I am alreay searching about this the entire day and I just don't know how to get it working. Basically I want to create a PDF server side with iText 7 in my ASP.Net WebApi. Very straightforward and easy Pdf creation:
[HttpGet]
public HttpResponseMessage CreateLieferschein()
{
MemoryStream stream = new MemoryStream();
PdfWriter writer = new PdfWriter(stream);
var pdf = new PdfDocument(writer);
var document = new Document(pdf);
document.Add(new Paragraph("Hello World!"));
}
From here on I have no idea on how to return the file from the controller. I would really appreciate any help since I am just lost.
I haven't tried this out, just doing it freehand so bear with me, but I think you can get the idea of what's going on here.
public HttpResponseMessage CreateLieferschein() {
// Create the itext pdf
MemoryStream stream = new MemoryStream();
PdfWriter writer = new PdfWriter(stream);
var pdf = new PdfDocument(writer);
var document = new Document(pdf);
document.Add(new Paragraph("Hello World!"));
document.Close(); // don't forget to close or the doc will be corrupt! ;)
// Load the mem stream into a StreamContent
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(stream)
};
// Prep the response with headers, filenames, etc.
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "WebApi2GeneratedFile.pdf"
};
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
ResponseMessageResult responseMessageResult = ResponseMessage(httpResponseMessage);
// Cross your fingers...
return responseMessageResult;
}
try this :
[HttpGet]
public HttpResponseMessage CreateLieferschein()
{
Document doc = new Document(iTextSharp.text.PageSize.LETTER, 10, 10, 42, 35);
byte[] buffer;
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest);
using(MemoryStream stream = new MemoryStream())
{
using(PdfWriter wri = PdfWriter.GetInstance(doc, mem))
{
PdfWriter writer = new PdfWriter(stream);
var pdf = new PdfDocument(writer);
var document = new Document(pdf);
document.Add(new Paragraph("Hello World!"));
}
buffer = stream.ToArray();
var contentLength = buffer.Length;
var statuscode = HttpStatusCode.OK;
response = Request.CreateResponse(statuscode);
response.Content = new StreamContent(new MemoryStream(buffer));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = contentLength;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=" + document.Name + ".pdf", out contentDisposition)) {
response.Content.Headers.ContentDisposition = contentDisposition;
}
} else {
var statuscode = HttpStatusCode.NotFound;
var message = String.Format("Unable to find file. file \"{0}\" may not exist.", docid);
var responseData = responseDataFactory.CreateWithOnlyMetadata(statuscode, message);
response = Request.CreateResponse((HttpStatusCode)responseData.meta.code, responseData);
}
return response;
}

MemoryStream PDF file in ASP.NET Core 2

Keep getting an error 'Cannot access a closed Stream.'
Does the file need to be physically saved on a server first, prior to being Streamed? I am doing the same with Excel file and it works just fine. Trying to use the same principle here with PDF file.
[HttpGet("ExportPdf/{StockId}")]
public IActionResult ExportPdf(int StockId)
{
string fileName = "test.pdf";
MemoryStream memoryStream = new MemoryStream();
// Create PDF document
Document document = new Document(PageSize.A4, 25, 25, 25, 25);
PdfWriter pdfWriter = PdfWriter.GetInstance(document, memoryStream);
document.Open();
document.Add(new Paragraph("Hello World"));
document.Close();
memoryStream.Position = 0;
return File(memoryStream, "application/pdf", fileName);
}
Are you using iText7? If yes, please add that tag onto your question.
using (var output = new MemoryStream())
{
using (var pdfWriter = new PdfWriter(output))
{
// You need to set this to false to prevent the stream
// from being closed.
pdfWriter.SetCloseStream(false);
using (var pdfDocument = new PdfDocument(pdfWriter))
{
...
}
var renderedBuffer = new byte[output.Position];
output.Position = 0;
output.Read(renderedBuffer, 0, renderedBuffer.Length);
...
}
}
Switched to iText& (7.1.1) version as per David Liang comment
Here is the code that worked for me, a complete method:
[HttpGet]
public IActionResult Pdf()
{
MemoryStream memoryStream = new MemoryStream();
PdfWriter pdfWriter = new PdfWriter(memoryStream);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument);
document.Add(new Paragraph("Welcome"));
document.Close();
byte[] file = memoryStream.ToArray();
MemoryStream ms = new MemoryStream();
ms.Write(file, 0, file.Length);
ms.Position = 0;
return File(fileStream: ms, contentType: "application/pdf", fileDownloadName: "test_file_name" + ".pdf");
}

Using CryptoStream with StreamContent c#

I would like to to read an image from file or blob storage and base64 encode it as a stream and then pass that stream to StreamContent. The following code times out:
[HttpGet, Route("{id}", Name = "GetImage")]
public HttpResponseMessage GetImage([FromUri] ImageRequest request)
{
var filePath = HostingEnvironment.MapPath("~/Areas/API/Images/Mr-Bean-Drivers-License.jpg");
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(filePath, FileMode.Open);
var cryptoStream = new CryptoStream(stream, new ToBase64Transform(), CryptoStreamMode.Write);
result.Content = new StreamContent(cryptoStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
I am able to get the following code to work without keeping the file as a stream and read it all into memory but I would like to avoid that.
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
using (var image = Image.FromStream(fileStream))
{
var memoryStream = new MemoryStream();
image.Save(memoryStream, image.RawFormat);
byte[] imageBytes = memoryStream.ToArray();
var base64String = Convert.ToBase64String(imageBytes);
result.Content = new StringContent(base64String);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
return result;
}
}
The problem here is you're passing CryptoStreamMode.Write to the constructor of CryptoStream whereas you should be passing CryptoStreamMode.Read because the CryptoStream is going to be read as the HttpResponseMessage is returned.
For more details about this, see Figolu's great explanation about the various usages of CryptoStream in this answer.

Categories