You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java同一PDF文件加解密报错:PDF Header未找到

Android PDF解密报错“PDF Header not found”排查与修复

问题描述

在Android Java中实现同一PDF文件的直接加密解密(不生成新文件,路径存入ROOM),加密功能正常,但解密时应用崩溃,报错PDF Header not found。

加密代码

btnSave.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        File file = new File(Consts.MERGE_FETCH_LOC, "LockedPDF");
        if (!file.exists()) {
            file.mkdir();
        }
        if (doc != null) {
            String src = doc.getDocPath();
            String pass = editText.getText().toString();
            try {
                String res = lock(src, pass);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        documentDAO.insert(doc);
                    }
                }).start();
                fileSaver.deleteTempImage(res);
                Toast.makeText(LockPDFActivity.this, "PDF Locked!", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(LockPDFActivity.this, MainFeaturesActivity.class));
                finishAffinity();
            } catch (IOException e) {
                Toast.makeText(LockPDFActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
        }
    }
});

private String lock(String src, String pass) throws IOException {
    //make a copy of current file
    File currentFile = new File(src);
    File temp = new File(getFilesDir(),currentFile.getName());
    PDFFileUtils.copyFile(this,currentFile, temp);
    try (PdfReader reader = new PdfReader(temp);
         FileOutputStream outputStream = new FileOutputStream(new File(src))) {
        WriterProperties writerProperties = new WriterProperties();
        writerProperties.setStandardEncryption(pass.getBytes(), pass.getBytes(), EncryptionConstants.ALLOW_PRINTING, EncryptionConstants.ENCRYPTION_AES_128);
        PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
        PdfDocument pdfDoc = new PdfDocument(reader, pdfWriter);
        pdfDoc.close();
        return temp.getAbsolutePath();
    }
}

解密代码

private String unLock(String src, String pass) {
    // Make a copy of the current file
    File currentFile = new File(src);
    Log.d("de_unlock", "unLock: "+currentFile.getName());
    File temp = new File(getFilesDir(), currentFile.getName());
    PDFFileUtils.copyFile(this, currentFile, temp);

    // Try to unlock the PDF file
    try (PdfReader reader = new PdfReader(src, new ReaderProperties().setPassword(pass.getBytes()));
         PdfWriter writer = new PdfWriter(temp.getAbsolutePath())) {
        PdfDocument pdfDocument = new PdfDocument(reader, writer);
        pdfDocument.close();
        return src;
    }
    catch (IOException e) {
        Log.e("UnlockPDFActivity", "Error unlocking PDF: " + e.getMessage());
        return null;
    }
}

问题分析

  1. 文件复制异步风险:如果PDFFileUtils.copyFile是异步实现,会导致文件未完全复制完成就开始读取,生成的加密文件损坏,解密时读取损坏文件触发“PDF Header not found”错误。
  2. 解密逻辑错误:当前解密代码仅将解密内容写入临时文件temp,未将解密内容覆盖回原文件src,等于未修改原文件;同时临时文件命名与原副本冲突,易引发覆盖错误。
  3. 编码不一致风险:密码使用getBytes()默认编码,不同设备编码差异可能导致解密时密码验证失败。

修复方案

1. 替换为同步文件复制方法

确保文件复制完成后再执行后续操作,避免文件不完整:

// 新增同步文件复制方法
private void copyFile(File source, File dest) throws IOException {
    // 目标文件已存在则先删除
    if (dest.exists()) {
        dest.delete();
    }
    try (InputStream in = new FileInputStream(source);
         OutputStream out = new FileOutputStream(dest)) {
        byte[] buffer = new byte[1024 * 4]; // 增大缓冲区提升复制效率
        int length;
        while ((length = in.read(buffer)) > 0) {
            out.write(buffer, 0, length);
        }
        out.flush();
    }
}

2. 修复加密代码

替换异步复制为同步复制,修改临时文件命名避免冲突:

private String lock(String src, String pass) throws IOException {
    File currentFile = new File(src);
    // 新增后缀区分原文件副本,避免重名覆盖
    File temp = new File(getFilesDir(), currentFile.getName() + "_original");
    // 使用同步复制方法
    copyFile(currentFile, temp);

    try (PdfReader reader = new PdfReader(temp);
         FileOutputStream outputStream = new FileOutputStream(src)) {
        WriterProperties writerProperties = new WriterProperties();
        // 指定UTF-8编码,避免密码编码不一致
        writerProperties.setStandardEncryption(
                pass.getBytes(StandardCharsets.UTF_8),
                pass.getBytes(StandardCharsets.UTF_8),
                EncryptionConstants.ALLOW_PRINTING,
                EncryptionConstants.ENCRYPTION_AES_128
        );
        PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
        PdfDocument pdfDoc = new PdfDocument(reader, pdfWriter);
        pdfDoc.close();
        return temp.getAbsolutePath();
    }
}

3. 修复解密代码

修正逻辑,将解密后的文件覆盖回原文件,优化临时文件处理:

private String unLock(String src, String pass) {
    File currentFile = new File(src);
    Log.d("de_unlock", "unLock: " + currentFile.getName());
    // 新增后缀区分解密临时文件
    File temp = new File(getFilesDir(), currentFile.getName() + "_decrypted");

    try (PdfReader reader = new PdfReader(src, 
             new ReaderProperties().setPassword(pass.getBytes(StandardCharsets.UTF_8)));
         PdfWriter writer = new PdfWriter(temp.getAbsolutePath())) {
        PdfDocument pdfDocument = new PdfDocument(reader, writer);
        pdfDocument.close();

        // 将解密后的临时文件覆盖回原文件
        copyFile(temp, currentFile);
        // 删除临时文件
        temp.delete();
        return src;
    } catch (IOException e) {
        Log.e("UnlockPDFActivity", "Error unlocking PDF: " + e.getMessage());
        // 出错时清理临时文件
        if (temp.exists()) {
            temp.delete();
        }
        return null;
    }
}

4. 权限检查(针对外部存储文件)

若操作的PDF位于外部存储,需确保已申请对应权限:

  • Android 9及以下:申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限
  • Android 10及以上:申请MANAGE_EXTERNAL_STORAGE权限,或使用媒体存储API

内容的提问来源于stack exchange,提问作者Hafeez Niazi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:37:01