PDF盖章场景下MemoryStream报错的替代实现方案咨询
问题描述
我正在开发护照PDF加盖系统用户名、日期及用户签名图片的功能,签名在SQL数据库中以varbinary类型存储,模型中对应byte数组。运行代码时在XImage image = XImage.FromStream(signatureImage);处报错:"MemoryStream's internal buffer cannot be accessed",即使不手动释放MemoryStream问题依然存在,求可行的替代方案。
以下是当前代码:
private async void button_ApprovePassport_Click(object sender, EventArgs e) { await ProcessPassportPdfAsync(); } private async Task ProcessPassportPdfAsync() { try { // Check if a document is selected if (gridView_PortalDocs.IsSelected()) { var PortalDoc = gridView_PortalDocs.GetSelectedDataRow<PortalDocumentInfoModel>(); if (PortalDoc != null) { EmployeePortalService EPS = new EmployeePortalService(); if (PortalDoc.PortalFileID == null) { cGlobal.ShowErrorMessage("PortalFileID is null. Cannot retrieve document."); return; } var Data = await EPS.GetDocument(PortalDoc.PortalFileID); if (Data != null && !string.IsNullOrEmpty(Data.FileData)) { // Convert base64 string to byte array byte[] fileBytes = Convert.FromBase64String(Data.FileData); // Create a memory stream from the byte array using (MemoryStream inputStream = new MemoryStream(fileBytes)) { // Process the PDF (stamp it) using (MemoryStream processedStream = StampPdf(inputStream)) { // Save the processed PDF to a temporary file string tempFilePath = Path.Combine(Path.GetTempPath(), $"StampedPassport_{Guid.NewGuid()}.pdf"); File.WriteAllBytes(tempFilePath, processedStream.ToArray()); // Open the PDF with the default viewer System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(tempFilePath) { UseShellExecute = true }); // Convert the processed stream back to a base64 string string processedBase64 = Convert.ToBase64String(processedStream.ToArray()); // Using the API for the document reupload here // Update the PortalFiles table here MessageBox.Show("Passport PDF processed and approved successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } else { cGlobal.ShowErrorMessage("The document data could not be retrieved from the server or is empty."); } } else { cGlobal.ShowErrorMessage("The selected document information is invalid."); } } else { cGlobal.ShowInfoMessage("Please select a document to process."); } } catch (Exception ex) { cGlobal.ShowErrorMessage($"An error occurred while processing the passport PDF: {ex.Message}"); } } private MemoryStream StampPdf(MemoryStream inputStream) { // Get the user's signature and first name from the controller // Use Stefi's for testing then switch to cGlobal.SystemUserID var SystemUserSignature = Controller.GetSystemUserSignature(345); // Check if the signature was retrieved successfully if (SystemUserSignature == null || SystemUserSignature.Count == 0) { throw new Exception("User signature not found. Please add one in Access Control."); } // Retrieve the first user's details (assuming there's at least one entry) var user = SystemUserSignature[0]; // Ensure we're using UTF-8 encoding (important for PdfSharp) Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Load the existing document PdfDocument document = PdfReader.Open(inputStream, PdfDocumentOpenMode.Modify); // Get the first page (you might want to stamp all pages) PdfPage page = document.Pages[0]; // Create graphics object for drawing XGraphics gfx = XGraphics.FromPdfPage(page); // Create fonts using system fonts XFont font = new XFont("Arial", 12); XFont largeFont = new XFont("Arial", 24); // Create a semi-transparent brush for the background of the stamp XColor stampColor = XColor.FromArgb(128, 255, 0, 0); // Semi-transparent red XBrush stampBrush = new XSolidBrush(stampColor); // Draw a rectangle for the stamp background gfx.DrawRectangle(stampBrush, 50, 50, 250, 100); // Add text for the stamp gfx.DrawString("APPROVED", largeFont, XBrushes.White, new XRect(50, 50, 250, 50), XStringFormats.Center); // Add approval details gfx.DrawString($"By: {user.Forename}", font, XBrushes.White, new XRect(60, 100, 230, 25), XStringFormats.TopLeft); gfx.DrawString($"Date: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", font, XBrushes.White, new XRect(60, 125, 230, 25), XStringFormats.TopLeft); // If the signature is available, draw it on the PDF if (user.Signature != null && user.Signature.Length > 0) { // Load the signature image from the byte array using (var signatureImage = new MemoryStream(user.Signature)) { XImage image = XImage.FromStream(signatureImage); gfx.DrawImage(image, 50, 150, 150, 50); // Position and size of the signature } } // Save the document to a new MemoryStream MemoryStream outputStream = new MemoryStream(); document.Save(outputStream, false); outputStream.Position = 0; // Reset stream position to the beginning return outputStream; }
替代解决方案
方案1:复制签名字节数组,确保流的独立性
问题可能是PdfSharp在保存文档时仍需访问签名流,但原流的缓冲区已不可用。通过复制字节数组,创建独立的MemoryStream:
if (user.Signature != null && user.Signature.Length > 0) { // 克隆签名字节数组,避免原数组被外部操作影响 byte[] signatureCopy = (byte[])user.Signature.Clone(); using (var signatureImage = new MemoryStream(signatureCopy)) { XImage image = XImage.FromStream(signatureImage); gfx.DrawImage(image, 50, 150, 150, 50); } }
方案2:通过临时文件加载签名图像
绕开流访问限制,将签名字节数组写入临时文件,再从文件加载XImage:
if (user.Signature != null && user.Signature.Length > 0) { string tempSigPath = Path.Combine(Path.GetTempPath(), $"sig_{Guid.NewGuid()}.png"); File.WriteAllBytes(tempSigPath, user.Signature); try { XImage image = XImage.FromFile(tempSigPath); gfx.DrawImage(image, 50, 150, 150, 50); } finally { // 确保临时文件被清理 if (File.Exists(tempSigPath)) File.Delete(tempSigPath); } }
方案3:使用Bitmap中转签名图像
将签名字节数组转为Bitmap后重新写入流,确保流格式正确且可访问:
// 需引入System.Drawing命名空间 if (user.Signature != null && user.Signature.Length > 0) { using (var ms = new MemoryStream(user.Signature)) using (Bitmap bmp = new Bitmap(ms)) using (var outputMs = new MemoryStream()) { // 重新保存为PNG格式,确保流的兼容性 bmp.Save(outputMs, System.Drawing.Imaging.ImageFormat.Png); outputMs.Position = 0; XImage image = XImage.FromStream(outputMs); gfx.DrawImage(image, 50, 150, 150, 50); } }
额外注意事项
- 验证
user.Signature是否为有效的图像字节流(如PNG/JPG),无效格式会导致流访问失败。 - 检查PdfSharp版本,旧版本可能存在流处理bug,升级到最新稳定版可解决部分问题。
内容的提问来源于stack exchange,提问作者William Sewell
相关产品推荐
相关产品推荐

