coobird / thumbnailator

Thumbnailator - a thumbnail generation library for Java
MIT License
5.17k stars 789 forks source link

"Image cannot be null" when using `Thumbnails.of(BufferedImage...)` #73

Closed GoogleCodeExporter closed 3 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

The code is quite simple 

    static public void makeThumbnail(InputStream is, OutputStream os, int width, int height) throws IOException {
        BufferedImage originalImage = ImageIO.read(is);
        Thumbnails.of(originalImage)
                .width(width)
                .height(height)
                .outputFormat("jpg")
                .toOutputStream(os);
    }

    @Test
    public void testGenThumb() throws IOException, InterruptedException {
        File f = new File("testdata/image_1.8m.jpg");
        Assert.assertTrue(f.exists());
        FileInputStream is = new FileInputStream(f);
        for (int i = 0; i < 1000; i++) {
            File out = File.createTempFile("aabb", ".jpg");
            out.deleteOnExit();
            FileOutputStream os = new FileOutputStream(out);
            ImageProcessor.makeThumbnail(is, os, 1000, 1000);
            os.close();
        }
    }
}

When the test running, an exception was raise:
java.lang.NullPointerException: Image cannot be null.
    at net.coobird.thumbnailator.tasks.io.BufferedImageSource.<init>(Unknown Source)
    at net.coobird.thumbnailator.Thumbnails$Builder$BufferedImageImageSourceIterator$1.next(Unknown Source)
    at net.coobird.thumbnailator.Thumbnails$Builder$BufferedImageImageSourceIterator$1.next(Unknown Source)
    at net.coobird.thumbnailator.Thumbnails$Builder.toOutputStream(Unknown Source)
    at com.xiaomi.mfs.ImageProcessor.makeThumbnail(ImageProcessor.java:30)
    at com.xiaomi.mfs.test.ImageTest.testGenThumb(ImageTest.java:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

If the file open failed, it should failed at
Assert.assertTrue(f.exists());

What version of the product are you using? On what operating system? Which
version of Java (Sun/Oracle? OpenJDK?) ?

java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

Please provide any additional information below.

Original issue reported on code.google.com by pcman.zh...@gmail.com on 23 Dec 2014 at 2:25

GoogleCodeExporter commented 9 years ago
Hello there,
Thank you taking the time to submit this report.

This is an issue caused by an improper usage of the 
`FileInputStream`/`InputStream`.
(Although I haven't had the chance to actually run the code, you can see my 
reasoning below.)

The `FileInputStream` outside of the `for` loop. This will cause the 
`InputStream` to be read to the end of the file on the first iteration of the 
`for` loop, and on the second iteration, the stream is already at the end of 
the file, so there's no image data to read.

The exception thrown by Thumbnailator is caused by the `BufferedImage` returned 
by the `ImageIO.read` method being `null`. This occurs, as the stream is at the 
end of file, which means there's no data beyond that point. Since there's no 
data, there's no appropriate `ImageReader` that can be used to read the image, 
therefore, the `read` method returns `null`. You can verify this line of 
reasoning by reading the documentation for the `read` method:
  http://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageIO.html#read%28java.io.InputStream%29

The `ImageIO.read` method does not make any assumptions about where the image 
is, so it will not automatically go back to the beginning of the stream to find 
image data -- and this is appropriate behavior. Imagine a case where the source 
file actually is a binary blob containing images and other data, and the 
`ImageIO.read` method is given a precisely-positioned `InputStream` as its 
source; in that case it would be inappropriate for the `ImageIO.read` method to 
perform any marker changes to the `InputStream`.

Bottom line is, it's up to the programmer to provide a properly prepared 
`InputStream` to a consumer of the `InputStream`. (At least, I believe that 
would be the assumption if not specified otherwise.)

To fix your test code, move the line that is instantiating your 
`FileInputStream` from the source `File` object to *inside* the `for` loop, and 
don't forget to close the `FileInputStream` before the end of the `for` loop, 
just so you release any resources that may be left open.

---

That said, there should be better `null` checking for the `Thumbnails.of` 
methods so that this kind of problem can be detected earlier rather than after 
going deeper into the Thumbnailator infrastructure, so that the stack trace can 
be simplified and to return better exception messages.

Original comment by coobird...@gmail.com on 23 Dec 2014 at 6:25

GoogleCodeExporter commented 9 years ago
Thank you very much. I'll have a try.

Original comment by pcman.zh...@gmail.com on 25 Dec 2014 at 9:33

sundayha commented 5 years ago

Jdk 1.8 has the same error, jdk 10 does not have this error