Writing Filled PDF Form to File - How do I dispose of Temp File?


#1

Hello All,

I am trying to use PDF.Kit to read form field names from a PDF form, fill the fields with data from a datatable in memory, and then write the file back out to disk using ASP.Net.

The problem is if an error occurs anywhere in this process a stale 0 bytelength output PDF is left over. When the user tries again an exception is thrown because the “file already exists”. I am using the following declaration:


Dim inForm As Form = New Form(Server.MapPath(ConfigurationSettings.AppSettings(“Template-” & custSeg).ToString), Server.MapPath(“Generated/” & ConfigurationSettings.AppSettings(“priceBookOutput”).ToString & “-” & Session.SessionID & “.pdf”))


I have to do this early on in my code as I have to read the input PDF form field names into a datatable so delcaring this later is not an option. I’ve already tried writing to a memory stream and then using a binarywriter to dump it to a file once the logic completes but there is a lot of form fields being filled in and it results in a 68% performance reduction (00:02:28s for a build as opposed to 00:01:40s using the above method) which is unacceptable as far as I’m concerned.

Is there a way to use the same direct file output method without leaving a stale file? I’ve already tried deleting the file from within Global_Error in my Global.asax (which is where my other custom error-handling resides) but this doesn’t work. Indeed, in order to rectify I must recycle the application pool in IIS in order to manually delete the offending 0 byte file on the server!

I’ll post more of my code if necessary but I think the above explanation will suffice.

Thanks for the help!

KRT



#2

Thank you for considering Aspose.

1) What class do you use to fill the datatable into the form? Please post the code about this.
2) There is a complete example in the demo of “Merge mails into
a local pdf file
”, you could refer to this code in the Demos directory of your installed directory.
3) Please refer to these url for a quick view of how to use the AutoFiller class: http://www.aspose.com/Products/Aspose.Pdf.Kit/Api/Aspose.Pdf.Kit.AutoFillerMembers.html
and http://www.aspose.com/Products/Aspose.Pdf.Kit/Api/Aspose.Pdf.Kit.AutoFiller.html . The output can be a pdf file or a stream.


#3

Ken,

Those examples are nice but one of the columns of the datatable has to be populated with fieldname read in from the PDF form template prior to populating the datatable with external data, and then filling in the template. In this way I’m not sure if autofiller is the way to go.

Is there a way to just force the Form method to overwrite a template that already exists or somehow unlock it? Should I just read in the form field names first and then open the form/create the output form after the lengthy datatable population?

Regards,
KRT


#4

Ken,

Here is the code I am using. The class I’m utilizing is Form.FillField.

Dim inForm As Form = New Form(Server.MapPath(ConfigurationSettings.AppSettings(“priceBookTemplate-” & custSeg).ToString)
, Server.MapPath(“Generated/” & ConfigurationSettings.AppSettings(“priceBookOutput”).ToString & “-” & Session.SessionID & “.pdf”))
Dim tableField As String

’ Now iterate through each form field and add the item SKUs into a datatable in memory
Dim skuTable As DataTable = New DataTable(“SKUData”)
Dim skuRow As DataRow

skuTable.Columns.Add(“SKU”, GetType(String))
skuTable.PrimaryKey = New DataColumn() {skuTable.Columns(0)}
skuTable.Columns.Add(“SIZE”, GetType(String))
skuTable.Columns.Add(“UNIT”, GetType(String))
skuTable.Columns.Add(“UNITWT”, GetType(String))
skuTable.Columns.Add(“UNITPALLET”, GetType(String))
skuTable.Columns.Add(“PALLETWT”, GetType(String))
skuTable.Columns.Add(“COLOR”, GetType(String))
skuTable.Columns.Add(“PRICE”, GetType(String))

For Each tableField In inForm.FieldsNames
If Split(tableField, "
")(0) <> “HEADER” Then ’ We do not want header rows in the SKU table
skuRow = skuTable.NewRow
skuRow.Item(“SKU”) = Split(tableField, “")(0)
Try
skuTable.Rows.Add(skuRow)
Catch ex As System.Data.ConstraintException
’ If the SKU already appears in the table, skip this fieldcode
End Try
End If
Next
skuRow = Nothing

’ Instantiate connection to 400 through encrypted tunnel
Dim talkTo400 As New Pricebook.GetItemData

talkTo400.HostName = ConfigurationSettings.AppSettings(“priceBookServer”).ToString
talkTo400.Library = ConfigurationSettings.AppSettings(“priceBookLibrary”).ToString
talkTo400.UserID = ConfigurationSettings.AppSettings(“priceBookUser”).ToString
talkTo400.Password = ConfigurationSettings.AppSettings(“priceBookPassword”).ToString
talkTo400.CLProgram = ConfigurationSettings.AppSettings(“priceBookProgram”).ToString

Try
talkTo400.Connect()
Catch ex As Exception
Throw New Exception(“An error occured when attempting to connect to the ERP System.”, ex)
End Try

Dim outArray(0) As String
Dim a As Decimal
Dim totalFields As Decimal = inForm.FieldsNames.Length

’ Populate the datatable

For Each skuRow In skuTable.Rows
Dim itemSku As String
Dim sData As String

’ If user cancels creation, abort.
If Application("cancelbuild
” & Session.SessionID) = True Then
talkTo400.Disconnect()
talkTo400 = Nothing
’ pbEntry.Close()
Response.Redirect(“default.aspx”)
End If

itemSku = skuRow.Item(“SKU”)

Try
outArray = talkTo400.RunProgram(itemSku, custNo)
Catch ex As Exception
Throw New Exception("An error occured while attempting to get item data for SKU: " & itemSku, ex)
End Try

sData = Trim(outArray(0).ToString)
If sData = “” Then
sData = “No Data”
End If
skuRow.Item(1) = sData

sData = Trim(outArray(1).ToString)
If sData = “” Then
sData = “No Data”
End If
skuRow.Item(2) = sData

sData = Trim(outArray(2).ToString)
If sData = “” Then
sData = “No Data”
End If
skuRow.Item(3) = sData

sData = Trim(CInt(outArray(3)).ToString)
If sData = “0” Then
sData = “No Data”
End If
skuRow.Item(4) = sData

sData = Trim(outArray(4).ToString)
If sData = “” Then
sData = “No Data”
End If
skuRow.Item(5) = sData

sData = Trim(outArray(5).ToString)
If sData = “” Then
sData = “N/A”
End If
skuRow.Item(6) = sData

’ Output the final price in USD with the specified GPM applied
If Session(“Pricebook_dGpm”) > 1 Then
sData = FormatCurrency(CDec(Session(“Pricebook_dGpm”)) * (CDec(outArray(6))))
Else
sData = FormatCurrency(CDec(outArray(6)))
End If

If Trim(sData) = “$0.00” Then
sData = “No Data”
End If
skuRow.Item(7) = sData

’ Update progressbar
a += (100 / ((totalFields - 4) / 7)) ’ Total # of field codes minus customer info ones multipled by two
’ because we run through them twice (divided by 7 because we’re counting unique SKUs)
Application(“progress_” & Session.SessionID) = CInt(a)
sData = Nothing
Next

’ Clean up
skuRow = Nothing
talkTo400.Disconnect()
talkTo400 = Nothing

’ For each field, determine if data from 400 or local dB is required - fill form fields in PDF
For Each tableField In inForm.FieldsNames
’ If user cancels creation, abort.
If Application(“cancelbuild_” & Session.SessionID) = True Then
’ pbEntry.Close()
Response.Redirect(“default.aspx”)
End If

’ Field names should look something like “0253-0025-22-1_ITEMNO”
If Not (Left(tableField, 6) = “HEADER”) Then
Dim itemSku As String = Split(tableField, “")(0)
Dim tableColumn As String = Split(tableField, "
”)(1)
Dim itemRow() As DataRow
itemRow = skuTable.Select(“SKU = '” & itemSku & “’”)

’ If so, grab it
Select Case tableColumn
Case “SIZE”
inForm.FillField(tableField, itemRow(0).Item(“SIZE”))
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “UNIT”
inForm.FillField(tableField, itemRow(0).Item(“UNIT”))
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “UNITWT”
inForm.FillField(tableField, itemRow(0).Item(“UNITWT”))
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “UNITPALLET”
inForm.FillField(tableField, itemRow(0).Item(“UNITPALLET”))
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “PALLETWT”
inForm.FillField(tableField, itemRow(0).Item(“PALLETWT”))
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “COLOR”
inForm.FillField(tableField, itemRow(0).Item(“COLOR”))
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “ITEMNO”
inForm.FillField(tableField, itemSku)
’ Make this field un-editable
inForm.FlattenField(tableField)
Case “PRICE”
inForm.FillField(tableField, itemRow(0).Item(“PRICE”))
’ Make this field un-editable if user wishes
If Session(“Pricebook_editable”) = 0 Then
inForm.FlattenField(tableField)
End If
End Select

’ Update progressbar
'a += (100 / ((totalFields - 4) * 2)) ’ Total # of field codes minus customer info ones multipled by two because
’ we run through them twice
'Application(“progress_” & Session.SessionID) = CInt(a)
Else
’ We will assume this is a front page field and fill as such
Dim customerReader As SqlDataReader
Dim sSql As String = "SELECT company, address, city, state, postal, phone FROM distributors WHERE cust_no = " & custNo
Dim customerCommand As New SqlCommand(sSql, conn)

customerReader = customerCommand.ExecuteReader()
If customerReader.Read() Then
Dim addressField As String = Split(tableField, “_”)(1)
Select Case addressField
Case “COMPANY”
inForm.FillField(tableField, Trim(customerReader.Item(0).ToString))
Case “ADDRESS”
inForm.FillField(tableField, Trim(customerReader.Item(1).ToString))
Case “CITY/STATE/ZIP”
inForm.FillField(tableField, Trim(customerReader.Item(2).ToString) & “, " & Trim(customerReader.Item(3).ToString) &
" " & Trim(customerReader.Item(4).ToString))
Case “PHONE”
inForm.FillField(tableField, Trim(customerReader.Item(5).ToString))
End Select
End If
customerReader.Close()
End If
Next

’ Tell progressbar we are done
Application("complete
” & Session.SessionID) = True

inForm.Save()
inForm = Nothing



#5

Hi, FastPlymouth
I have read your code and think that your code is ok. After you call inForm.Save(), the output file is closed and no temp file is created.

Please provide your debug info including your input file name ,output file name and “already existed file name”.

Best regards.