วันพฤหัสบดีที่ ๒๒ ตุลาคม พ.ศ. ๒๕๕๒

ASP.NET รวม Crystal Report 2 รายงานเป็น pdf ด้วย iTexSharp

คราวนี้เป็นตอนที่ 3 สำหรับ iTextSharp ครับ เราจะสร้าง pdf file จาก Crystal Report (ต่อจากนี้จะเรียกย่อๆว่า CR) 2 ไฟล์ มารวมกันให้เป็นไฟล์เดียว ในการทดสอบผมใช้ CR ตัวเดียว แต่สร้างเป็น 2 report สำหรับเอามารวมกันเป็น pdf ลองดูโค้ดกันครับ


Imports CrystalDecisions.CrystalReports.Engine
Imports XXEntity = jnithi.CLASS.XX.Entity
Imports XXManager = jnithi.CLASS.XXManager

Partial Class frmTestITextSharp
Inherits System.Web.UI.Page

Private Sub CreateReport()
'สร้าง CR1
Dim myReport1 As New ReportDocument()
Dim rpt1Data = XXManager.XXOutStandingManager.GetXXOutStandings(0, Nothing, Nothing, "R00939", "")
myReport1.FileName = Server.MapPath("~/Reports/XX/rptXXOutstanding.rpt")
myReport1.SetDataSource(rpt1Data)
myReport1.SetParameterValue(0, Date.Today.ToString("dd-MMM-yyyy"))
myReport1.SetParameterValue(1, "")
myReport1.SetParameterValue(2, "Client")

' สร้าง CR2
Dim myReport2 As New ReportDocument
Dim rpt2Data = XXManager.XXOutStandingManager.GetXXOutStandings(0, Nothing, Nothing, "A01032", "")
myReport2.FileName = Server.MapPath("~/Reports/XX/rptXXOutstanding.rpt")
myReport2.SetDataSource(rpt2Data)
myReport2.SetParameterValue(0, Date.Today.ToString("dd-MMM-yyyy"))
myReport2.SetParameterValue(1, "")
myReport2.SetParameterValue(2, "Client")

' สร้าง pdfReader1 ชื่อ reader1 โดย export stream จาก CR1
Dim s1 As System.IO.Stream = myReport1.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat)
Dim reader1 As New iTextSharp.text.pdf.PdfReader(s1)

' สร้าง pdfReader2 ชื่อ reader2 โดย export stream จาก CR2
Dim s2 As System.IO.Stream = myReport2.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat)
Dim reader2 As New iTextSharp.text.pdf.PdfReader(s2)

' สร้าง pdfCopy โดยกำหนด os เป็น output FileStream เพื่อสร้าง File ชื่อ rptMerge.pdf
Dim pdfDoc As New iTextSharp.text.Document()
Dim os As New System.IO.FileStream(Server.MapPath("~/rptMerge.pdf"), IO.FileMode.Create)
Dim pdfCopy As New iTextSharp.text.pdf.PdfCopy(pdfDoc, os)

pdfDoc.Open() ' เปิด pdf Document เพื่อเริ่มการ copy

' วนลูปอ่าน reader1 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader1.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader1, iPage))
Next

' วนลูปอ่าน reader2 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader2.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader2, iPage))
Next

' Clear Memory
pdfDoc.Close()
pdfCopy.Close()
reader1.Close()
reader2.Close()
os.Close()
os.Dispose()
s1.Close()
s1.Dispose()
s2.Close()
s2.Dispose()

Response.Redirect("~/rptMerge.pdf")

End Sub

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
CreateReport()
End Sub
End Class


เมื่อเราใช้ iTextSharp สร้าง pdf ไฟล์เสร็จก็สั่ง redirect เพื่อเปิดไฟล์ที่ browser ครับ ทีแรกผมทดสอบให้ iTextSharp สร้าง pdf เป็น MemoryStream แล้วลองใช้ Response.BinaryWrite กับ Response.OutputStream.Write เพื่อสร้าง HTTPResponse แต่ไม่สามารถทำได้ครับ พอเปิดไฟล์ Browser จะฟ้องว่าไฟล์เสีย ลอง search ใน google ดูหลายๆคนพบปัญหาเดียวกัน เลยแก้ด้วยการใช้ FileStream เพื่อสร้าง temp file แทนตามตัวอย่างด้านบนครับ

ลองดูโค้ด MemoryStream ที่มีปัญหาดู เผื่อมีคนแก้ได้ครับ

' โค้ดด้านบนเหมือนเดิม เริ่มเปลี่ยนตั้งแต่ Dim os ลงมา
' เปลี่ยนจาก FileStream เป็น MemoryStream
'Dim os As New System.IO.FileStream(Server.MapPath("~/rptMerge.pdf"), IO.FileMode.Create)
Dim os As New System.IO.MemoryStream()

Dim pdfCopy As New iTextSharp.text.pdf.PdfCopy(pdfDoc, os)
pdfDoc.Open() ' เปิด pdf Document เพื่อเริ่มการ copy

' วนลูปอ่าน reader1 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader1.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader1, iPage))
Next

' วนลูปอ่าน reader2 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader2.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader2, iPage))
Next

Response.Clear()
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition", "attachment;filename=Export.pdf")
Response.BinaryWrite(os.ToArray)

' Clear Memory
pdfDoc.Close()
pdfCopy.Close()
reader1.Close()
reader2.Close()
os.Close()
os.Dispose()
s1.Close()
s1.Dispose()
s2.Close()
s2.Dispose()
Response.End()


blog ก่อนหน้านี้มีคนบอกอยากให้ comment ด้วย คราวนี้ก็เลยใส่ comment มา แต่ไม่แน่ใจว่าจะละเอียดพอหรือเปล่าครับ

บทความก่อนหน้า
เขียนโปรแกรมจัดการเอกสาร pdf ด้วย iTextSharp
การจัดการเอกสาร pdf ด้วย iTextSharp(2)

1 ความคิดเห็น:

Unknown กล่าวว่า...

ขอบคุณมากครับ สำหรับบทความดีช่วยผมได้เยอะจริงๆครับ ส่วนผมลองทดสอบ MemoryStream แล้วต้องแก้ดังนี้ ให้ Clear Memory ทั้งหมดก่อน เหลือเฉพาะ os (MemoryStream) ให้ Clear หลัง Response ครับ แต่ผมไม่ได้ทดสอบจาก code ของคุณนะครับ ลองดูนะครับ

' Clear Memory
pdfDoc.Close()
pdfCopy.Close()
reader1.Close()
reader2.Close()
s1.Close()
s1.Dispose()
s2.Close()
s2.Dispose()


Response.Clear()
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition", "attachment;filename=Export.pdf")
Response.BinaryWrite(os.ToArray)
Response.End()

' Clear Memory
os.Close()
os.Dispose()