FileInputStream 和 ByteArrayInputStream 的区别

Difference between FileInputStream and ByteArrayInputStream

我尝试使用两种方式读取文件类型。它在使用 ByteArrayInputStream 但不是 FileInputStream

时工作

FileInputStreamURLConnection

结合使用
String fileType = URLConnection
  .guessContentTypeFromStream(
    new FileInputStream(new File("C:\image.jpeg"))
   ); //fileType = null

ByteArrayInputStreamURLConnection

结合使用
String fileType = URLConnection
.guessContentTypeFromStream(
  new ByteArrayInputStream(Files.readAllBytes(new File("C:\image.jpeg").toPath()))
 ); //fileType = image/jpeg

为什么结果不同? 另外,有没有提到只使用 ByteArrayInputStream 来读取文件类型?

虽然这两种输入流类型在很多方面都不同,但这种行为的原因仅与这两种流的 mark/reset 支持有关。

如果您查看 URLConnection.guessContentTypeFromStream 的来源,您会注意到:

// If we can't read ahead safely, just give up on guessing
if (!is.markSupported())
    return null;

并且 ByteArrayInputStreammarkSupported 覆盖为 return trueFileInputStream 继承默认的 InputStream.markSupported 方法 returns false.

换句话说,guessContentTypeFromStream 无法使用文件输入流(或任何不支持 mark/reset 的流)。

URLConnection.guessContentTypeFromStream 的技术是查看第一个字节,即所谓的 magic cookie 来识别文件。

实施者选择让 Stream 保持不变,因此继续阅读将从(再次)开始。

为此,它会对先前标记的流位置(实际开始)执行 reset():

static public String guessContentTypeFromStream(InputStream is)
                    throws IOException {
    // If we can't read ahead safely, just give up on guessing
    if (!is.markSupported())
        return null;

    is.mark(16);
    int c1 = is.read();
    int c2 = is.read();
    int c3 = is.read();
    ...
    int c14 = is.read();
    int c15 = is.read();
    int c16 = is.read();
    is.reset();
    ....

对于顺序 FileInputStream markSupported() returns 默认 false.

可以通过用 BufferedInputStream 包裹 FileInputStream 来解决它,无论如何这样会更快。

String fileType = URLConnection
    .guessContentTypeFromStream(
        new BufferedInputStream(Files.newInputStream(Paths.get("C:\image.jpeg")))
     );

请注意,javadoc 中所述的 Files.newInputStream 将不支持标记要重置的位置。

(使用ByteArrayInputStream开销太大。)