【问题标题】:Strange byte[] behavior reading from a URL从 URL 读取的奇怪 byte[] 行为
【发布时间】:2012-09-24 22:16:03
【问题描述】:

最后,我的终极目标是:

  • 从 URL 读取(这个问题是关于什么的)
  • 将检索到的 [PDF] 内容保存到数据库中的 BLOB 字段(已经确定)
  • 从 BLOB 字段中读取并将该内容附加到电子邮件中
  • 无需进入文件系统

以下方法的目标是获得一个byte[],可以作为电子邮件附件在下游使用(以避免写入磁盘):

public byte[] retrievePDF() {

         HttpClient httpClient = new HttpClient();

         GetMethod httpGet = new GetMethod("http://website/document.pdf");
         httpClient.executeMethod(httpGet);
         InputStream is = httpGet.getResponseBodyAsStream();

         byte[] byteArray = new byte[(int) httpGet.getResponseContentLength()];

         is.read(byteArray, 0, byteArray.length);

        return byteArray;
}

对于特定的 PDF,getResponseContentLength() 方法返回 101,689 作为长度。 奇怪部分是如果我设置一个断点并询问byteArray变量,它有101,689个字节元素,但是,在字节#3744之后,数组的剩余字节全为零(@ 987654326@)。 PDF 阅读器客户端(如 Adob​​e Reader)无法读取生成的 PDF。

为什么会这样?

通过浏览器检索相同的 PDF 并将其保存到磁盘,或使用类似以下的方法(我以 answer to this StackOverflow post 为模板),生成可读的 PDF:

public void retrievePDF() {
    FileOutputStream fos = null;
    URL url;
    ReadableByteChannel rbc = null;

    url = new URL("http://website/document.pdf");

    DataSource urlDataSource = new URLDataSource(url);

    /* Open a connection, then set appropriate time-out values */
    URLConnection conn = url.openConnection();
    conn.setConnectTimeout(120000);
    conn.setReadTimeout(120000);

    rbc = Channels.newChannel(conn.getInputStream());

    String filePath = "C:\\temp\\";
    String fileName = "testing1234.pdf";
    String tempFileName = filePath + fileName;

    fos = new FileOutputStream(tempFileName);
    fos.getChannel().transferFrom(rbc, 0, 1 << 24);
    fos.flush();

    /* Clean-up everything */
    fos.close();
    rbc.close();
}

对于这两种方法,在 Windows 中执行 右键单击 > 属性... 时,生成的 PDF 的大小为 101,689 字节。

为什么字节数组会中途“停止”?

【问题讨论】:

    标签: java http url bytearray channel


    【解决方案1】:

    InputStream.read 最多可读取 byteArray.length 字节,但可能不会读取那么多。它返回它读取的字节数。您应该重复调用它以完全读取数据,如下所示:

    int bytesRead = 0;
    while (true) {
        int n = is.read(byteArray, bytesRead, byteArray.length);
        if (n == -1) break;
        bytesRead += n;
    }
    

    【讨论】:

    • 如果 bytesRead >= byteArray.length 会发生什么?我认为将返回零,您将陷入无限循环。
    • 正确。这假设 byteArray.length >= 流中的总字节数,在这种情况下基于服务器给出的内容长度。所以我想如果服务器是恶意的并决定在内容长度上撒谎,理论上这可能会发生,但可能不会。
    • 我试过了,我确实通过这种方式得到了更多——97,943 字节——但数组仍然将 101,689 的剩余字节显示为零。此外,生成的 PDF 仍然无法打开。
    • 对不起,我得到了 97,944 字节。 (我没有计算“零”字节。)
    • @PattMauler - 请注意,当您从 StackOverflow 复制粘贴代码时,请确保您替换要替换的代码。 (提示:101689 - 97944 = 3745;没错……您从“其他”调用中读取的字节对is.read 忘记取出。)
    【解决方案2】:

    检查InputStream.read的返回值。它不会一口气读完。你必须写一个循环。或者,更好的是,使用 Apache Commons IO 来复制流。

    【讨论】:

      【解决方案3】:

      101689 = 2^16 + 36153 所以看起来,缓冲区大小有 16 位限制。 36153 和 3744 的区别可能是因为 header 部分已经被读取到一个 1K 左右的超小缓冲区中,并且已经包含了一些字节。

      【讨论】:

        猜你喜欢
        • 2011-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-19
        • 2013-05-10
        • 2023-01-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多