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

从SOAP响应InputStream截取PDF生成文件格式无效,求排查指导

问题分析:截取SOAP响应中的PDF文件无效的原因与解决方案

嘿,我来帮你看看问题出在哪!你遇到的PDF文件无法打开的问题,核心原因是用文本处理逻辑操作二进制的PDF数据,这直接破坏了文件的字节结构,再加上截取范围的小问题,导致生成的PDF格式无效。咱们一步步拆解:

核心错误点

  • 用Reader处理二进制数据是致命错误
    PDF是二进制文件,而InputStreamReader、BufferedReader是专门处理文本的工具。它们会把字节按默认编码(系统编码)转换成字符,这个过程中会丢失或篡改二进制里的非文本字节,直接破坏PDF的结构。

  • 字符串操作二进制内容会损坏数据
    你把整个流转成StringBuilder再做截取,字符串是基于字符的,而二进制数据的字节和字符并非一一对应,这种操作会导致字节错位;最后用getBytes()转回去时,默认编码又会再次破坏原始二进制数据。

  • 截取范围不准确
    你定位的是&lt;File&gt;(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:36:13