Java:在小程序中从URL读取PDF文件到字节数组/字节缓冲区

8
我正在努力弄清楚为什么这段代码不起作用。我有一个applet,它应该使用pdf-renderer库读取.pdf并显示它,但出于某种原因,当我读取位于我的服务器上的.pdf文件时,它们最终会变得损坏。我通过将文件重新写出来进行了测试。
我已经尝试在IE和Firefox中查看applet,并且出现了损坏的文件。有趣的是,当我尝试在Safari(Windows版)中查看applet时,文件实际上是正常的!我知道JVM可能不同,但我仍然感到困惑。我编译的是Java 1.5。JVM是1.6。下面是读取文件的代码片段。
public static ByteBuffer getAsByteArray(URL url) throws IOException {
        ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();

        URLConnection connection = url.openConnection();
        int contentLength = connection.getContentLength();
        InputStream in = url.openStream();
        byte[] buf = new byte[512];
        int len;
        while (true) {
            len = in.read(buf);
            if (len == -1) {
                break;
            }
            tmpOut.write(buf, 0, len);
        }
        tmpOut.close();
        ByteBuffer bb = ByteBuffer.wrap(tmpOut.toByteArray(), 0,
                                        tmpOut.size());
        //Lines below used to test if file is corrupt
        //FileOutputStream fos = new FileOutputStream("C:\\abc.pdf");
        //fos.write(tmpOut.toByteArray());
        return bb;
}

我一定是漏掉了什么,一直在努力思考解决方法。非常感谢任何帮助。谢谢。


编辑: 为了进一步阐明我的情况,在使用代码片段读取文件之前和之后的差异在于,我输出后读取的文件比原始文件小得多。打开它们时,它们不能被识别为.pdf文件。没有抛出我忽略的异常,并且我尝试过刷新也无济于事。

这个代码片段在Safari中可以工作,这意味着文件被完整地读取,大小没有区别,并且可以用任何.pdf阅读器打开。在IE和Firefox中,文件总是损坏的,大小始终相同较小。

我监视 len 变量(读取一个59kb的文件时),希望看到每次循环读取多少字节。在IE和Firefox中,在18kb处,in.read(buf)返回-1,就好像文件已经结束了。Safari不会这样做。

我会继续努力,非常感谢迄今为止提供的所有建议。


当你说文件损坏时,你具体指的是什么?如果与原始文件进行比较,有何不同之处? - Eddie
请回答 Eddie 问题的第二部分。 另外,contentLength 的值是否正确? - jdigital
3个回答

12

如果这些小的更改有所区别,可以尝试以下方法:

public static ByteBuffer getAsByteArray(URL url) throws IOException {
    URLConnection connection = url.openConnection();
    // Since you get a URLConnection, use it to get the InputStream
    InputStream in = connection.getInputStream();
    // Now that the InputStream is open, get the content length
    int contentLength = connection.getContentLength();

    // To avoid having to resize the array over and over and over as
    // bytes are written to the array, provide an accurate estimate of
    // the ultimate size of the byte array
    ByteArrayOutputStream tmpOut;
    if (contentLength != -1) {
        tmpOut = new ByteArrayOutputStream(contentLength);
    } else {
        tmpOut = new ByteArrayOutputStream(16384); // Pick some appropriate size
    }

    byte[] buf = new byte[512];
    while (true) {
        int len = in.read(buf);
        if (len == -1) {
            break;
        }
        tmpOut.write(buf, 0, len);
    }
    in.close();
    tmpOut.close(); // No effect, but good to do anyway to keep the metaphor alive

    byte[] array = tmpOut.toByteArray();

    //Lines below used to test if file is corrupt
    //FileOutputStream fos = new FileOutputStream("C:\\abc.pdf");
    //fos.write(array);
    //fos.close();

    return ByteBuffer.wrap(array);
}

你忘了关闭fos,如果你的应用程序仍在运行或突然终止,这可能导致该文件变短。此外,我添加了创建适当初始大小的ByteArrayOutputStream。(否则Java将不得不重复分配新数组并复制、分配新数组并复制,这是昂贵的)。请用更合适的值替换16384的值。16k对于PDF来说可能太小了,但我不知道你期望下载的“平均”大小是多少。

由于你两次使用了toByteArray()(即使其中一个是在诊断代码中),我将其赋给了一个变量。最后,虽然这不应该有任何区别,但当你将整个数组包装在ByteBuffer中时,你只需要提供字节数组本身。提供偏移量0和长度是多余的。

请注意,如果你以这种方式下载PDF文件,请确保你的JVM正在运行具有足够大的堆的状态,以便你有足够的空间来读取几倍于你期望的最大文件大小。你正在使用的方法将整个文件保存在内存中,只要你能承受那些内存就可以。


0

我以为我和你有同样的问题,但事实证明我的问题是我假设你总是得到完整的缓冲区直到你什么都没有得到。但你不应该这样假设。 网上的例子(例如java2s/tutorial)使用了BufferedInputStream。但对我来说没有任何区别。

你可以检查一下在循环中是否实际上得到了完整的文件。那么问题就在ByteArrayOutputStream中。


0
你在关闭 tmpOut 流之前尝试过使用 flush() 确保所有字节都被写出了吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接