【发布时间】:2020-09-27 10:52:10
【问题描述】:
我目前正在开发一个将文本文件转换为 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, percent, i);
}
pdf.Close();
writer.Flush();
return resultStream;
}
PDF 文件本身甚至不是 1000 KB(它的 882 KB),但这对我来说很奇怪。我错过了什么吗?考虑到当我尝试使用转换后的文件本身时,它不会对内存造成任何问题,这就更奇怪了。
【问题讨论】:
-
您不会处理您创建的任何一次性物品。另外,例如在
ConvertFile()中,您将返回一个字节数组,因此相关的TextToPdf()应该返回一个字节数组,而不是MemoryStream。在这里,您还必须处理您在此过程中创建的 MemoryStream 对象。 -- 更改resultStream.Position = 0;中的resultStream.Seek(0, SeekOrigin.Begin);-- 可能,将这些字节数组刷新到磁盘,在 System Temp 文件夹中,只在内存中保留指针和元数据。 -
@Jimi 所以经过大量重构和更改后,我设法解决了内存问题,关闭内存流并没有太大帮助,但是将字节写入临时文件并只保留路径(FileInfo)到那个文件完全解决了这个问题。谢谢!
-
您不必只 close MemorySteam 对象,您必须 Dispose() of ALL 这些对象,如
StreamReader可能还有一些(或全部)PdfWriter/PdfDocumentthings (我不知道这些是否有Dispose()方法:如果有,你有 调用它)。可能使用using语句/嵌套using块来声明一次性对象:使用(var ms = new MemoryStream()) using (var streamReader = new StreamReader(stream); { // your code};等)。即使在该代码中引发了异常,它们也会处理这些对象。 -
@Jimi 是的,你完全正确,我很笨,我什至没有意识到我需要关闭 itext7 对象,我认为
document.Close()会为我做这件事。结果根本不是。我只是担心,所有嵌套的 using 块都可以吗? -
当然可以,这是标准做法。请注意,使用
using语句声明的所有对象都将自动 处理,因此不要在using块内添加例如streamReader.Close()(与Dispose()相同) (s)。有时,根据操作,您可能需要(或发现更好).Flush()一个 Stream 事先。
标签: c# itext7 .net-framework-4.8