从SOAP响应InputStream截取PDF生成文件格式无效,求排查指导
问题分析:截取SOAP响应中的PDF文件无效的原因与解决方案
嘿,我来帮你看看问题出在哪!你遇到的PDF文件无法打开的问题,核心原因是用文本处理逻辑操作二进制的PDF数据,这直接破坏了文件的字节结构,再加上截取范围的小问题,导致生成的PDF格式无效。咱们一步步拆解:
核心错误点
用Reader处理二进制数据是致命错误
PDF是二进制文件,而InputStreamReader、BufferedReader是专门处理文本的工具。它们会把字节按默认编码(系统编码)转换成字符,这个过程中会丢失或篡改二进制里的非文本字节,直接破坏PDF的结构。字符串操作二进制内容会损坏数据
你把整个流转成StringBuilder再做截取,字符串是基于字符的,而二进制数据的字节和字符并非一一对应,这种操作会导致字节错位;最后用getBytes()转回去时,默认编码又会再次破坏原始二进制数据。截取范围不准确
你定位的是<File>(HTML转义后的<File>),但PDF的实际起始是这一行之后的%PDF-1.4;另外你截取到EOF+3,但PDF的结束标记是%%EOF,漏了前面的两个%,还可能把后面的MIME分隔符也包含进去,进一步损坏文件。
正确解决方案:直接操作字节流
我们要避开文本处理,直接用字节流定位和提取PDF内容,这里有两种可靠方式:
方式一:解析MIME多部分消息(推荐)
你的响应是标准MIME多部分格式,用专门的库解析能准确找到Content-ID: <File>的部分,避免手动截取的误差。可以用Apache Commons的MimeMultipart:
import org.apache.commons.mail.ByteArrayDataSource; import javax.mail.internet.MimeMultipart; import javax.mail.BodyPart; // 你的输入流 InputStream inputStream = ...; MimeMultipart multipart = new MimeMultipart(new ByteArrayDataSource(inputStream, "multipart/related")); for (int i = 0; i < multipart.getCount(); i++) { BodyPart part = multipart.getBodyPart(i); String[] contentIds = part.getHeader("Content-ID"); if (contentIds != null && "<File>".equals(contentIds[0])) { // 写入PDF文件 File pdfFile = new File(Environment.getExternalStorageDirectory(), "abc123.pdf"); try (OutputStream output = new FileOutputStream(pdfFile)) { part.writeTo(output); } Toast.makeText(this, "文件创建成功!", Toast.LENGTH_SHORT).show(); break; } }
方式二:手动定位字节范围(无第三方依赖)
如果不想引入库,可以手动读取字节,精准找到PDF的起始和结束标记:
import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.util.Arrays; import java.nio.charset.StandardCharsets; InputStream inputStream = ...; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; // 先把所有字节读入内存 while ((len = inputStream.read(buffer)) != -1) { baos.write(buffer, 0, len); } byte[] allBytes = baos.toByteArray(); // 定位PDF起始标记 "%PDF-1.4" String pdfStart = "%PDF-1.4"; byte[] startBytes = pdfStart.getBytes(StandardCharsets.ISO_8859_1); int startIndex = -1; for (int i = 0; i <= allBytes.length - startBytes.length; i++) { boolean match = true; for (int j = 0; j < startBytes.length; j++) { if (allBytes[i + j] != startBytes[j]) { match = false; break; } } if (match) { startIndex = i; break; } } // 定位PDF结束标记 "%%EOF" String pdfEnd = "%%EOF"; byte[] endBytes = pdfEnd.getBytes(StandardCharsets.ISO_8859_1); int endIndex = -1; for (int i = allBytes.length - endBytes.length; i >= 0; i--) { boolean match = true; for (int j = 0; j < endBytes.length; j++) { if (allBytes[i + j] != endBytes[j]) { match = false; break; } } if (match) { endIndex = i + endBytes.length; break; } } // 截取并写入文件 if (startIndex != -1 && endIndex != -1) { byte[] pdfBytes = Arrays.copyOfRange(allBytes, startIndex, endIndex); File pdfFile = new File(Environment.getExternalStorageDirectory(), "abc123.pdf"); try (OutputStream output = new FileOutputStream(pdfFile)) { output.write(pdfBytes); } Toast.makeText(this, "文件创建成功!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "未找到有效PDF内容", Toast.LENGTH_SHORT).show(); }
为什么这样能解决问题?
- 全程操作原始字节流,避免了文本编码转换带来的二进制数据损坏;
- 精准定位PDF的起始和结束标记,确保截取的是完整、正确的PDF内容;
- 写入文件时直接使用原始字节,完全保留了PDF的结构信息。
内容的提问来源于stack exchange,提问作者kodali
相关产品推荐
相关产品推荐

