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; } }
问题分析
- 文件复制异步风险:如果
PDFFileUtils.copyFile是异步实现,会导致文件未完全复制完成就开始读取,生成的加密文件损坏,解密时读取损坏文件触发“PDF Header not found”错误。 - 解密逻辑错误:当前解密代码仅将解密内容写入临时文件
temp,未将解密内容覆盖回原文件src,等于未修改原文件;同时临时文件命名与原副本冲突,易引发覆盖错误。 - 编码不一致风险:密码使用
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
相关产品推荐
相关产品推荐

