小型pdf文件变成大字节数组

问题描述

我目前正在开发一个小型应用程序,该程序可将文本文件转换为PDF文件或反向转换为PDF文件。但是,我希望能够将转换后的文件保留在内存中,直到用户按下按钮以保存文件(或将一组文件保存为.zip)为止,所有转换后的文件都以其旧路径作为键保存在字典中并将字节数组作为值。

一切正常,除了出于测试目的,我拿了一个大文本文件,包含12000+行,并试图在文本和PDF之间来回移动,现在我遇到了一个奇怪的问题。

使用这种大文件从文本转换为PDF时,一切都很好。

但是,从该文件的PDF格式到文本格式会在堆中占用大量内存。最终超过2 GB导致内存不足异常。

我应该注意我正在使用Itext 7。

这是我正在使用的代码

文本为PDF

        public override byte[] ConvertFile(Stream stream,string path)
        {
            OnFileStartConverting(path);
            string ext = Path.GetExtension(path);
            TextFileType current = TextFileType.Parse(ext);
            MemoryStream resultStream = new MemoryStream();

            if (current.Extension.Equals(TextFileType.Txt.Extension))
            {
                resultStream = TextToPdf(stream,path);
            }
            else if (current.Extension.Equals(TextFileType.Word.Extension))
            {
                throw new NotImplementedException();
            }

            OnFileConverted(path);
            return resultStream.ToArray();
        }

        private MemoryStream TextToPdf(Stream stream,string path)
        {
            MemoryStream resultStream = new MemoryStream();
            StreamReader streamReader = new StreamReader(stream);
            int lineCount = GetNumberOfLines(streamReader);
            PdfWriter writer = new PdfWriter(resultStream);
            PdfDocument pdf = new PdfDocument(writer);
            Document document = new Document(pdf);

            int lineNumber = 1;
            while (!streamReader.EndOfStream)
            {
                string line = streamReader.ReadLine();
                Paragraph paragraph = new Paragraph(line);
                document.Add(paragraph);
                int percent = lineNumber * 100 / lineCount;
                OnFileConverting(path,percent,lineNumber);
                lineNumber++;
            }

            document.Close();
            return resultStream;
        }

PDF到文本

        public override byte[] ConvertFile(Stream stream,string path)
        {
            OnFileStartConverting(path);

            string ext = Path.GetExtension(path);
            TextFileType current = TextFileType.Parse(ext);
            MemoryStream resultStream = new MemoryStream();

            if (current.Extension.Equals(TextFileType.Pdf.Extension))
            {
                resultStream = PdfToText(stream,path);
            }
            else if (current.Extension.Equals(TextFileType.Word.Extension))
            {
                throw new NotImplementedException();
            }

            resultStream.Seek(0,SeekOrigin.Begin);
            OnFileConverted(path);

            return resultStream.ToArray();
        }

        private MemoryStream PdfToText(Stream stream,string path)
        {
            MemoryStream resultStream = new MemoryStream();
            StreamWriter writer = new StreamWriter(resultStream);

            PdfReader reader = new PdfReader(stream);
            PdfDocument pdf = new PdfDocument(reader);
            FilteredEventListener listener = new FilteredEventListener();
            LocationTextExtractionStrategy extractionStrategy =
                listener.AttachEventListener(new LocationTextExtractionStrategy());
            PdfCanvasProcessor parser = new PdfCanvasProcessor(listener);
            int numberOfPages = pdf.GetNumberOfPages();

            for (int i = 1; i <= numberOfPages; i++)
            {
                parser.ProcesspageContent(pdf.GetPage(i));
                writer.WriteLine(extractionStrategy.GetResultantText());
                int percent = i * 100 / numberOfPages;
                OnFileConverting(path,i);
            }

            pdf.Close();
            writer.Flush();

            return resultStream;
        }

从PDF转换为文本时的内存使用情况

enter image description here

PDF文件本身甚至还不到1000 KB(它的882 KB),但这对我来说很奇怪。我想念什么吗?当我尝试使用转换后的文件本身时,这更奇怪了,它不会对内存造成任何问题。

解决方法

问题的原因在于PdfToText,该文件对于多页文档提取的文本多于文本。

LocationTextExtractionStrategy在开始向其提供新页面时不会忘记其内容。它并非旨在跨页面重复使用,您应该为每个页面创建一个新实例。

代码循环中的重复使用会导致

  • 对于i=1,将第1页的内容写入writer
  • i=2第1和第2页的内容写入writer
  • i=3第1、2和3页的内容写入writer
  • ...

因此,请勿跨页面重复使用文本提取策略。取而代之的是将FilteredEventListenerLocationTextExtractionStrategyPdfCanvasProcessor的实例化到循环中,以便为每个页面重新创建它们。