如何确定文件是否为PDF文件?

我在Java中使用PdfBox从PDF文件中提取文本。 提供的某些输入文件无效,PDFTextStripper会暂停这些文件。 是否有一种干净的方法来检查提供的文件是否确实是有效的PDF?

你可以找到一个文件(或字节数组)的mime类型,所以你不要愚蠢地依赖扩展。 我是用光盘的MimeExtractor( http://aperture.sourceforge.net/ )做的,或者我前几天看到了一个图书馆( http://sourceforge.net/projects/mime-util

我使用光圈从各种文件中提取文本,不仅仅是pdf,而且必须调整pdf例如(光圈使用pdfbox,但我在pdfbox失败时添加了另一个库作为后备)

以下是我在NUnit测试中使用的内容,它必须针对使用Crystal Reports生成的多个PDF版本进行validation:

public static void CheckIsPDF(byte[] data) { Assert.IsNotNull(data); Assert.Greater(data.Length,4); // header Assert.AreEqual(data[0],0x25); // % Assert.AreEqual(data[1],0x50); // P Assert.AreEqual(data[2],0x44); // D Assert.AreEqual(data[3],0x46); // F Assert.AreEqual(data[4],0x2D); // - if(data[5]==0x31 && data[6]==0x2E && data[7]==0x33) // version is 1.3 ? { // file terminator Assert.AreEqual(data[data.Length-7],0x25); // % Assert.AreEqual(data[data.Length-6],0x25); // % Assert.AreEqual(data[data.Length-5],0x45); // E Assert.AreEqual(data[data.Length-4],0x4F); // O Assert.AreEqual(data[data.Length-3],0x46); // F Assert.AreEqual(data[data.Length-2],0x20); // SPACE Assert.AreEqual(data[data.Length-1],0x0A); // EOL return; } if(data[5]==0x31 && data[6]==0x2E && data[7]==0x34) // version is 1.4 ? { // file terminator Assert.AreEqual(data[data.Length-6],0x25); // % Assert.AreEqual(data[data.Length-5],0x25); // % Assert.AreEqual(data[data.Length-4],0x45); // E Assert.AreEqual(data[data.Length-3],0x4F); // O Assert.AreEqual(data[data.Length-2],0x46); // F Assert.AreEqual(data[data.Length-1],0x0A); // EOL return; } Assert.Fail("Unsupported file format"); } 

这里是NinjaCross代码的改编Java版本。

 /** * Test if the data in the given byte array represents a PDF file. */ public static boolean is_pdf(byte[] data) { if (data != null && data.length > 4 && data[0] == 0x25 && // % data[1] == 0x50 && // P data[2] == 0x44 && // D data[3] == 0x46 && // F data[4] == 0x2D) { // - // version 1.3 file terminator if (data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x33 && data[data.length - 7] == 0x25 && // % data[data.length - 6] == 0x25 && // % data[data.length - 5] == 0x45 && // E data[data.length - 4] == 0x4F && // O data[data.length - 3] == 0x46 && // F data[data.length - 2] == 0x20 && // SPACE data[data.length - 1] == 0x0A) { // EOL return true; } // version 1.3 file terminator if (data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x34 && data[data.length - 6] == 0x25 && // % data[data.length - 5] == 0x25 && // % data[data.length - 4] == 0x45 && // E data[data.length - 3] == 0x4F && // O data[data.length - 2] == 0x46 && // F data[data.length - 1] == 0x0A) { // EOL return true; } } return false; } 

还有一些简单的unit testing:

 @Test public void test_valid_pdf_1_3_data_is_pdf() { assertTrue(is_pdf("%PDF-1.3 CONTENT %%EOF \n".getBytes())); } @Test public void test_valid_pdf_1_4_data_is_pdf() { assertTrue(is_pdf("%PDF-1.4 CONTENT %%EOF\n".getBytes())); } @Test public void test_invalid_data_is_not_pdf() { assertFalse(is_pdf("Hello World".getBytes())); } 

如果你想出任何失败的unit testing,请告诉我。

由于您使用PDFBox,您可以简单地执行:

 PDDocument.load(file); 

如果PDF损坏等,它将以exception失败。

如果成功,您还可以检查PDF是否使用.isEncrypted()加密

你必须尝试这个….

 public boolean isPDF(File file){ file = new File("Demo.pdf"); Scanner input = new Scanner(new FileReader(file)); while (input.hasNextLine()) { final String checkline = input.nextLine(); if(checkline.contains("%PDF-")) { // a match! return true; } } return false; } 

Pdf文件以“%PDF”开头(在TextPad或类似文件中打开一个并查看)

你有什么理由不能用StringReader读取文件并检查这个吗?

我正在使用我在这里和其他网站/post上提出的一些建议来确定pdf是否有效。 我故意破坏了pdf文件,不幸的是,许多解决方案都没有检测到文件已损坏。

最后,在修改了API中的不同方法之后,我尝试了这个:

 PDDocument.load(file).getPage(0).getContents().toString(); 

这没有抛出exception,但确实输出了这个:

  WARN [COSParser:1154] The end of the stream doesn't point to the correct offset, using workaround to read the stream, stream start position: 171, length: 1145844, expected end position: 1146015 

就个人而言,如果文件已损坏我想要抛出exception,以便我自己处理它,但似乎我正在实现的API已经以自己的方式处理它们。

为了解决这个问题,我决定尝试使用提供热门语句的类(COSParser)来解析文件。 我发现有一个名为PDFParser的子类,它inheritance了一个名为“setLenient”的方法,这是关键( https://pdfbox.apache.org/docs/2.0.4/javadocs/org/apache/pdfbox/pdfparser /COSParser.html )。

然后我实现了以下内容:

  RandomAccessFile accessFile = new RandomAccessFile(file, "r"); PDFParser parser = new PDFParser(accessFile); parser.setLenient(false); parser.parse(); 

根据我的意愿,这为我损坏的文件抛出了一个Exception。 希望这可以帮助别人!

也许我来不及回答。 但你应该看看蒂卡。 它在内部使用PDFBox Parser来解析PDF

您只需要导入tika-app-latest * .jar

  public String parseToStringExample() throws IOException, SAXException, TikaException { Tika tika = new Tika(); try (InputStream stream = ParsingExample.class.getResourceAsStream("test.pdf")) { return tika.parseToString(stream); // This should return you the pdf's text } } 

这将是一个更清洁的解决方案。 您可以在这里参考Tika用法的更多细节: https : //tika.apache.org/1.12/api/

Roger Keays的答案是错的! 因为并非版本1.3中的所有PDF文件都不是由EOL终止的。 以下答案适用于所有未损坏的pdf文件:

 public static boolean is_pdf(byte[] data) { if (data != null && data.length > 4 && data[0] == 0x25 && // % data[1] == 0x50 && // P data[2] == 0x44 && // D data[3] == 0x46 && // F data[4] == 0x2D) { // - // version 1.3 file terminator if (//data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x33 && data[data.length - 7] == 0x25 && // % data[data.length - 6] == 0x25 && // % data[data.length - 5] == 0x45 && // E data[data.length - 4] == 0x4F && // O data[data.length - 3] == 0x46 && // F data[data.length - 2] == 0x20 // SPACE //&& data[data.length - 1] == 0x0A// EOL ) { return true; } // version 1.3 file terminator if (//data[5] == 0x31 && data[6] == 0x2E && data[7] == 0x34 && data[data.length - 6] == 0x25 && // % data[data.length - 5] == 0x25 && // % data[data.length - 4] == 0x45 && // E data[data.length - 3] == 0x4F && // O data[data.length - 2] == 0x46 // F //&& data[data.length - 1] == 0x0A // EOL ) { return true; } } return false; } 

一般来说,我们可以这样,任何pdf版本都会以%% EOF结束,所以我们可以像下面这样查看。

 public static boolean is_pdf(byte[] data) { String s = new String(data); String d = s.substring(data.length - 7, data.length - 1); if (data != null && data.length > 4 && data[0] == 0x25 && // % data[1] == 0x50 && // P data[2] == 0x44 && // D data[3] == 0x46 && // F data[4] == 0x2D) { // - if(d.contains("%%EOF")){ return true; } } return false; } 

有一个非常方便和简单的库来测试PDF内容: https : //github.com/codeborne/pdf-test

API非常简单:

 import com.codeborne.pdftest.PDF; import static com.codeborne.pdftest.PDF.*; import static org.junit.Assert.assertThat; public class PDFContainsTextTest { @Test public void canAssertThatPdfContainsText() { PDF pdf = new PDF(new File("src/test/resources/50quickideas.pdf")); assertThat(pdf, containsText("50 Quick Ideas to Improve your User Stories")); } }