首页 文章

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

提问于
浏览
28

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

11 回答

  • 9

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

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

  • 1

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

    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");
        }
    
  • 6

    这里是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;
    }
    

    还有一些简单的单元测试:

    @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()));
    }
    

    如果你想出任何失败的单元测试,请告诉我 .

  • 1

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

    PDDocument.load(file);
    

    如果PDF损坏等,它将以异常失败 .

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

  • 5

    你必须尝试这个....

    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;
    }
    
  • 4

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

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

  • 20

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

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

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

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

    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
    

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

    为了解决这个问题,我决定尝试使用给出warm语句的类(COSParser)来解析文件 . 我发现有一个名为PDFParser的子类,它继承了一个名为"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 . 希望这可以帮助别人!

  • 2

    也许我来不及回答 . 但是你应该看看Tika . 它在内部使用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/

  • -1

    罗杰凯斯的答案是错的!因为并非版本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;
    }
    
  • 0

    一般来说,我们可以这样,任何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;
        }
    
  • 5

    有一个非常方便和简单的库来测试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"));
      }
    }
    

相关问题