This is my code which is used in a wcf service. It successfully generates the PDF, but after generating the document, the folder in which the PDF is generated gives the error: "access is denied"
The PDF is closed for the website, but for the continuous web service it is not closed.
string r = builder.ToString();
string pdfname = Fuhre + "_" + ProtokolType + "_" + GeraeteNr + "_" + r;
PdfWriter.GetInstance(document, new FileStream(#"C:\inetpub\wwwroot\protokoll_pdfs\"+pdfname+".pdf",FileMode.Create));
document.Open();
WebClient wc = new WebClient();
string htmlText = html;
//Response.Write(htmlText);
List<IElement> htmlarraylist = HTMLWorker.ParseToList(new StringReader(htmlText), null);
for (int k = 0; k < htmlarraylist.Count; k++)
{
document.Add((IElement)htmlarraylist[k]);
}
pdflink1 = pdfname + ".pdf";
htmlpdflink =""+pdflink1;
document.Close();
You need to be careful to dispose of everything.
using(var filesStream = new FileStream())
{
using(PdfWriter wri = PdfWriter.GetInstance(doc, fileStream))
{
...
}
}
There are a few other objects (Stream, Document) you may want to close. A sample of how to perform the operation is shown below.
FileStream stream = new FileStream(filePath, FileMode.CreateNew);
Document doc = new Document(PageSize.A4, 24, 24, 24, 24);
PdfWriter writer = PdfWriter.GetInstance(doc, stream);
doc.Open();
//PDF writing operations here
writer.Flush();
doc.Close();
writer.Close();
stream.Close();
You want to serve a file to a browser and/or you want to save the file on disk.
In that case, you would benefit from creating the file in memory and then send the bytes to the browser as well as to a file on disk. This is explained in the answer to the following question: Pdf file not loading properly created by the servlet
The answer mentioned above is written in Java, so you'll have to adapt it.
You can do so by looking at other examples. For instance: Create PDF in memory instead of physical file
byte[] pdfBytes;
using(var mem = new MemoryStream())
{
using(PdfWriter wri = PdfWriter.GetInstance(doc, mem))
{
doc.Open();//Open Document to write
Paragraph paragraph = new Paragraph("This is my first line using Paragraph.");
Phrase pharse = new Phrase("This is my second line using Pharse.");
Chunk chunk = new Chunk(" This is my third line using Chunk.");
doc.Add(paragraph);
doc.Add(pharse);
doc.Add(chunk);
}
pdfBytes = mem.ToArray();
}
Now you can write the pdfBytes to the Response object in your web application:
private void ShowPdf(byte[] strS)
{
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=" + DateTime.Now);
Response.BinaryWrite(strS);
Response.End();
Response.Flush();
Response.Clear();
}
And you can reuse the bytes to write them to a File: Can a Byte[] Array be written to a file in C#?
File.WriteAllBytes(string path, byte[] bytes)
If the problem persist, then you know that it isn't caused by iTextSharp, because iTextSharp merely produces the bytes.
In addition to what everyone else said, I strongly recommend you break each process into multiple sections that don't interact and aren't even aware of each other. Then test each section independently as much as possible. For instance:
private void makePDF( filePath )
{
//Create the PDF
}
private void main()
{
//Make the PDF
makePDF( "test.pdf" );
//If this line fails then your makePDF code has an open handle
File.Delete( "test.pdf" );
}
Then continue:
private void makePDF( filePath )
{
//Create the PDF
}
private void emailPDF( filePath )
{
//Email the PDF
}
private void main()
{
//Make the PDF
makePDF( "test.pdf" );
emailPDF( "test.pdf" );
//If this line fails now then your emailPDF code has an open handle
File.Delete( "test.pdf" );
}
The important part if that you don't try 500 things all at once because that leads to "something is locking my file but I don't know what".
Related
I have this simple piece of code which I took from another question here with the same topic, corrupted PDF files.
I've tried to implement the described solution, along with other references, but have met no sucess yet.
Here are the two functions generating my example PDF:
private void ShowPdf (byte[] str)
{
var Response = HttpContext.Current.Response;
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=" + DateTime.Now);
Response.BinaryWrite(str);
Response.End();
Response.Flush();
Response.Clear();
}
private byte[] CreatePDF2()
{
Document doc = new Document(PageSize.LETTER, 50, 50, 50, 50);
using (MemoryStream output = new MemoryStream())
{
PdfWriter wri = PdfWriter.GetInstance(doc, output);
doc.Open();
Paragraph header = new Paragraph("Test bug") { Alignment = Element.ALIGN_CENTER };
Paragraph paragraph = new Paragraph("test.");
Phrase phrase = new Phrase("testnewline. \nnewline hapenned.");
Chunk chunk = new Chunk("Chucnk cauncuanocnaacoocsinasiocniocsanacsoi chunk.");
doc.Add(header);
doc.Add(paragraph);
doc.Add(phrase);
doc.Add(chunk);
doc.Close();
return output.ToArray();
}
}
Then at an request, I just consume it as in:
ShowPdf(CreatePDF2());
The problem is that this generates a file called "Response.pdf.pdf", which is corrupt and can't be opened.
How can I solve this problem?
Obs.: I am currently using iTextSharp 4.1.6
Try this for output variable,
FileStream output = new FileStream(Server.MapPath("MyFirstPDF.pdf"), FileMode.Create);
I have an issue with trying to create a large PDF file. Basically I have a list of byte arrays, each containing a PDF in a form of a byte array. I wanted to merge the byte arrays into a single PDF. This works great for smaller files (under 2000 pages), but when I tried creating a 12,00 page file it bombed). Originally I was using MemoryStream but after some research, a common solution was to use a FileStream instead. So I tried a file stream approach, however get similar results. The List contains 3,800 records, each containing 4 pages. MemoryStream bombs after around 570. FileStream after about 680 records. The current file size after the code crashed was 60MB. What am I doing wrong? Here is the code I have, and the code crashes on "copy.AddPage(curPg);" directive, inside the "for(" loop.
private byte[] MergePDFs(List<byte[]> PDFs)
{
iTextSharp.text.Document doc = new iTextSharp.text.Document();
byte[] completePDF;
Guid uniqueId = Guid.NewGuid();
string tempFileName = Server.MapPath("~/" + uniqueId.ToString() + ".pdf");
//using (MemoryStream ms = new MemoryStream())
using(FileStream ms = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
iTextSharp.text.pdf.PdfCopy copy = new iTextSharp.text.pdf.PdfCopy(doc, ms);
doc.Open();
int i = 0;
foreach (byte[] PDF in PDFs)
{
i++;
// Create a reader
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(PDF);
// Cycle through all the pages
for (int currentPageNumber = 1; currentPageNumber <= reader.NumberOfPages; ++currentPageNumber)
{
// Read a page
iTextSharp.text.pdf.PdfImportedPage curPg = copy.GetImportedPage(reader, currentPageNumber);
// Add the page over to the rest of them
copy.AddPage(curPg);
}
// Close the reader
reader.Close();
}
// Close the document
doc.Close();
// Close the copier
copy.Close();
// Convert the memorystream to a byte array
//completePDF = ms.ToArray();
}
//return completePDF;
return GetPDFsByteArray(tempFileName);
}
A couple of notes:
PdfCopy implements iDisposable, so you should try and see if a using helps.
PdfCopy.FreeReader() will help.
Anyway, not sure if you're using MVC or WebForms, but here's a simple working HTTP handler tested with a 15 page 125KB test file that runs on my workstation:
<%# WebHandler Language="C#" Class="MergeFiles" %>
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
public class MergeFiles : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
List<byte[]> pdfs = new List<byte[]>();
var pdf = File.ReadAllBytes(context.Server.MapPath("~/app_data/test.pdf"));
for (int i = 0; i < 4000; ++i) pdfs.Add(pdf);
var Response = context.Response;
Response.ContentType = "application/pdf";
Response.AddHeader(
"content-disposition",
"attachment; filename=MergeLotsOfPdfs.pdf"
);
Response.BinaryWrite(MergeLotsOfPdfs(pdfs));
}
byte[] MergeLotsOfPdfs(List<byte[]> pdfs)
{
using (var ms = new MemoryStream())
{
using (Document document = new Document())
{
using (PdfCopy copy = new PdfCopy(document, ms))
{
document.Open();
for (int i = 0; i < pdfs.Count; ++i)
{
using (PdfReader reader = new PdfReader(
new RandomAccessFileOrArray(pdfs[i]), null))
{
copy.AddDocument(reader);
copy.FreeReader(reader);
}
}
}
}
return ms.ToArray();
}
}
public bool IsReusable { get { return false; } }
}
Tried to make the output file similar to what you described in the question, but YMMV, depending on how large the individual PDFs you're dealing with are in size. Here's the test output from my run:
So after a lot of messing around, I realized that there just was no way around it. However, I did manage to find a work-around. Instead of returning byte array, I return a temp file path, which I then transmit and delete there after.
private string MergeLotsOfPDFs(List<byte[]> PDFs)
{
Document doc = new Document();
Guid uniqueId = Guid.NewGuid();
string tempFileName = Server.MapPath("~/__" + uniqueId.ToString() + ".pdf");
using (FileStream ms = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
PdfCopy copy = new PdfCopy(doc, ms);
doc.Open();
int i = 0;
foreach (byte[] PDF in PDFs)
{
i++;
// Create a reader
PdfReader reader = new PdfReader(new RandomAccessFileOrArray(PDF), null);
// Cycle through all the pages
for (int currentPageNumber = 1; currentPageNumber <= reader.NumberOfPages; ++currentPageNumber)
{
// Read a page
PdfImportedPage curPg = copy.GetImportedPage(reader, currentPageNumber);
// Add the page over to the rest of them
copy.AddPage(curPg);
// This is a lie, it still costs money, hue hue hue :)~
copy.FreeReader(reader);
}
reader.Close();
}
// Close the document
doc.Close();
// Close the document
copy.Close();
}
// Return temp file path
return tempFileName;
}
And here is how I send that data to the client.
// Send the merged PDF file to the user.
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
Response.ClearHeaders();
response.ContentType = "application/pdf";
response.AddHeader("Content-Disposition", "attachment; filename=1094C.pdf;");
response.WriteFile(tempFileName);
HttpContext.Current.Response.Flush(); // Sends all currently buffered output to the client.
DeleteFile(tempFileName); // Call right after flush but before close
HttpContext.Current.Response.SuppressContent = true; // Gets or sets a value indicating whether to send HTTP content to the client.
HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.
Lastly, here is a fancy DeleteFile method
private void DeleteFile(string fileName)
{
if (File.Exists(fileName))
{
try
{
File.Delete(fileName);
}
catch (Exception ex)
{
//Could not delete the file, wait and try again
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Delete(fileName);
}
catch
{
//Could not delete the file still
}
}
}
}
This question is related to one I asked here on "How can I prompt the user for a save location from a Sharepoint page?"
What I need to do is, after generating a PDF thus:
private void GeneratePDF(List<ListColumns> listOfListItems)
{
. . .
//Create a stream that we can write to, in this case a MemoryStream
StringBuilder sb = new StringBuilder();
using (var ms = new MemoryStream())
{
using (var doc = new Document(PageSize.A4, 25, 25, 10, 10))
{
//Create a writer that's bound to our PDF abstraction and our stream
using (var writer = PdfWriter.GetInstance(doc, ms))
{
//Open the document for writing
doc.Open();
Paragraph docTitle = new Paragraph("UCSC - Direct Payment Form", timesBold16UCSCBlue);
doc.Add(docTitle);
Paragraph subTitle = new Paragraph("(Not to be used for reimbursement of services)", timesRoman9Font);
subTitle.IndentationLeft = 20;
doc.Add(subTitle);
. . . // tons of iTextSharp PDF-generation code elided for brevity
String htmlToRenderAsPDF = sb.ToString();
//XMLWorker also reads from a TextReader and not directly from a string
using (var srHtml = new StringReader(htmlToRenderAsPDF))
{
XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
}
doc.Close();
}
}
try
{
var bytes = ms.ToArray();
// This is currently saved to a location on the server such as C:\Users\TEMP.SP.005\Desktop\iTextSharpTest.pdf (but the number (such as
"005" irregularly increments))
String pdfFileID = GetYYYYMMDDAndUserNameAndAmount();
String pdfFileName = String.Format("DirectPayDynamic_{0}.pdf", pdfFileID);
String fileFullpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), pdfFileName);
String fileLinkBase = "Generated PDF: {1}";
String filelink = String.Format(fileLinkBase, fileFullpath, pdfFileName);
File.WriteAllBytes(fileFullpath, bytes);
var pdflink = new Label
{
CssClass = "finaff-webform-field-label",
Text = filelink
};
this.Controls.Add(pdflink);
}
catch (DocumentException dex)
{
throw (dex);
}
catch (IOException ioex)
{
throw (ioex);
}
catch (Exception ex)
{
throw (ex);
}
}
} // GeneratePDF
...afford the user the opportunity to save that file to their local machine (currently, it is being saved on the server).
IOW, what I basically want to do is replace this line:
String fileFullpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), pdfFileName);
...with something like:
String fileFullpath = "Hey, bloke (or blokette), where do you want to save this?"
This is obviously possible, and even somewhat common, as many websites will generate files for you and then allow you to save those
generated files to either a location you select or a default location, such as your downloads folder.
Either a server-side (C#) or a client-side (jQuery) solution is acceptable, although I prefer server-side, as that is where the PDF is being generated.
When I had to force an Excel spreadsheet to download to the client, I used this code, which may point you to where you are wanting to get:
Control xxx of type 'LinkButton' must be placed inside a form tag with runat=server
protected void ExportExcelFile(object Sender, EventArgs e) { //export to excel
var grdResults = new DataGrid(); // REF: https://stackoverflow.com/q/28155089/153923
if (periodCriteria.SelectedValue == "year") {
grdResults = RollupDG;
} else {
grdResults = QuarterDG;
}
var response = HttpContext.Current.Response;
response.Clear();
response.Charset = String.Empty;
response.ContentType = "application/vnd.ms-excel";
response.AddHeader("Content-Disposition", "attachment; filename=GlBudgetReport.xls");
using (var sw = new StringWriter()) {
using (var htw = new HtmlTextWriter(sw)) {
grdResults.RenderControl(htw);
response.Write(sw.ToString());
response.End();
}
}
}
I am trying to download multiple pdf's as attachments in my asp.net application.I have created some templates and filling values using pdfstamper(itextsharp). I am able to fill the values but not able to download.
private void FillForm(string path, DataTable BridgeValues, DataTable Comments, DataTable Maintenance,string Newfilename)
{
try
{
string pdfTemplate = path;
string newFile = Newfilename;
string Pathser = "";
if (!System.IO.Directory.Exists(Server.MapPath(#"~/PDF/")))
{
System.IO.Directory.CreateDirectory(Server.MapPath(#"~/PDF/"));
}
if (Directory.Exists(Server.MapPath(#"~/PDF/")))
{
Pathser = Server.MapPath(#"~/PDF/" + Newfilename);
}
System.IO.MemoryStream mStream = new System.IO.MemoryStream();
// create a new PDF reader based on the PDF template document
PdfReader pdfReader = new PdfReader(pdfTemplate);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(Pathser, FileMode.Create));
AcroFields pdfFormFields = pdfStamper.AcroFields;
DataColumn dc = null;
for (int i = 0; i < BridgeValues.Columns.Count - 1; i++)
{
dc = BridgeValues.Columns[i];
pdfFormFields.SetField(dc.ColumnName.ToString(), BridgeValues.Rows[0][dc].ToString());
}
pdfStamper.FormFlattening = true;
// close the pdf
pdfStamper.Close();
////Response.ContentType = "application/octet-stream";
Response.ContentType = "application/pdf";
////Response.AddHeader("Content-Disposition", "attachment; filename=Report.pdf");
Response.AddHeader("Content-Disposition", "attachment; filename=" + Newfilename + "");
////Response.BinaryWrite(mStream.ToArray());
Response.TransmitFile(Server.MapPath(("~/PDF/"+ Newfilename)));
Response.Clear();
Response.End();
}
catch (System.Threading.ThreadAbortException lException)
{
// do nothing
}
}
First time I tried to create one pdf ,it worked but later when I tried to download multiple files it gave an execption.
Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.
I don't think you can download multiple files by making one request and returning a collection from the action. I would suggest that it you need to allow user to download multiple files you ZIP them and stream the archive down to the browser.
Here is an example of zipping multiple files: http://devpinoy.org/blogs/keithrull/archive/2008/01/25/how-to-create-zip-files-in-c-with-sharpziplib-ziplib.aspx
I am trying to merge 2 crystal reports into single pdf file and I'm using Itextsharp v5.1.1. But it says the document cannot be opened. It might be corrupted. There are no build errors. but the pdf is malformed and cant be opened. Here is the order I chose to accomplish this.
Export the Crystal report to MemoryStream1 in pdf format
Export the second report into MemoryStream2.
Merge the Memory Streams
Send the Streams to Http Output Response as PDF.
Here is the Code for each step in the order.
/// Get the Dataset from Stored Procedure for the CSSource Report
dsCS = CSData.GetUSSourceXML(ds_input);
/// Create the Report of type CSsource
rptCS = ReportFactory.GetReport(typeof(CSsource));
rptCS .SetDataSource(dsCS);
/// Set the Parameters to the CS report
rptCS .ParameterFields["Parameterid"].CurrentValues.Clear();
rptCS .SetParameterValue("Parameterid", PID);
//// Serialize the Object as PDF
msCS=(MemoryStream)rptCS .ExportToStream(ExportFormatType.PortableDocFormat);
For Step 2
/// Get the Dataset from Stored Procedure for the Aden Report
dsAd = CSData.GetAdden(ds_input);
/// Create the Report of type Aden
rptAd = ReportFactory.GetReport(typeof(Aden));
rptAd.SetDataSource(dsAd );
/// Set the Parameters to the Aden report
rptAd.ParameterFields["Parameterid"].CurrentValues.Clear();
rptAd.SetParameterValue("Parameterid", PID);
//// Serialize the Object as PDF
msAD = (MemoryStream)rptAd.ExportToStream(ExportFormatType.PortableDocFormat);
For Step 3
System.Collections.Generic.List<byte[]> sourceFiles = new List<byte[]>();
sourceFiles.Add(msCS.ToArray());
sourceFiles.Add(msAD.ToArray());
PdfMerger mpdfs = new PdfMerger();
/// ms is the Memory stream to which both the streams are added
ms=mpdfs.MergeFiles(sourceFiles);
MergeFiles method is as follows
public MemoryStream MergeFiles(Generic.List<byte[]> sourceFiles)
{
Document document = new Document();
MemoryStream output = new MemoryStream();
try
{
// Initialize pdf writer
PdfWriter writer = PdfWriter.GetInstance(document, output);
//writer.PageEvent = new PdfPageEvents();
// Open document to write
document.Open();
PdfContentByte content = writer.DirectContent;
// Iterate through all pdf documents
for (int fileCounter = 0; fileCounter < sourceFiles.Count;
fileCounter++)
{
// Create pdf reader
PdfReader reader = new PdfReader(sourceFiles[fileCounter]);
int numberOfPages = reader.NumberOfPages;
// Iterate through all pages
for (int currentPageIndex = 1; currentPageIndex <=
numberOfPages; currentPageIndex++)
{
// Determine page size for the current page
document.SetPageSize(
reader.GetPageSizeWithRotation(currentPageIndex));
// Create page
document.NewPage();
PdfImportedPage importedPage =
writer.GetImportedPage(reader, currentPageIndex);
// Determine page orientation
int pageOrientation =
reader.GetPageRotation(currentPageIndex);
if ((pageOrientation == 90) || (pageOrientation == 270))
{
content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0,
reader.GetPageSizeWithRotation(currentPageIndex).Height);
}
else
{
content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
}
}
}
}
catch (Exception exception)
{
throw new Exception("There has an unexpected exception" +
" occured during the pdf merging process.", exception);
}
finally
{
// document.Close();
}
return output;
}
Step 4 to Serialize the Memory stream as PDF
// ms is the memory stream which is to be converted to PDF
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.Charset = string.Empty;
Response.AddHeader("Content-Disposition",
"attachment; filename=" +
"Application of " + FullName.Trim() + ".pdf");
//Write the file directly to the HTTP content output stream.
Response.OutputStream.Write(ms.ToArray(), 0,
Convert.ToInt32(ms.Length));
Response.OutputStream.Flush();
Response.OutputStream.Close();
rptCS.Close();
rptCS.Dispose();
rptAd.Close();
rptAd.Dispose();
Thanks for all those Developers helping me with this.
This is Urgent because it has to go production in a day or 2.
Please respond.
Chandanan.
Here's a simple merge method that copies PDF files into one PDF. I use this method quite often when merging pdfs. Hope it helps.
public MemoryStream MergePdfForms(List<byte[]> files)
{
if (files.Count > 1)
{
PdfReader pdfFile;
Document doc;
PdfWriter pCopy;
MemoryStream msOutput = new MemoryStream();
pdfFile = new PdfReader(files[0]);
doc = new Document();
pCopy = new PdfSmartCopy(doc, msOutput);
doc.Open();
for (int k = 0; k < files.Count; k++)
{
pdfFile = new PdfReader(files[k]);
for (int i = 1; i < pdfFile.NumberOfPages + 1; i++)
{
((PdfSmartCopy)pCopy).AddPage(pCopy.GetImportedPage(pdfFile, i));
}
pCopy.FreeReader(pdfFile);
}
pdfFile.Close();
pCopy.Close();
doc.Close();
return msOutput;
}
else if (files.Count == 1)
{
return new MemoryStream(files[0]);
}
return null;
}
Step 4 try:
rptCS.Close();
rptCS.Dispose();
rptAd.Close();
rptAd.Dispose();
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition",
"attachment; filename=" +
"Application of " + FullName.Trim() + ".pdf");
Response.BinaryWrite(ms.ToArray());
ApplicationInstance.CompleteRequest();