Search code examples
javajakarta-mail

Jakarta mail IMAP Could not download photo


Jakarta mail could not read email photo

I don't know how to handle it.

InputStream x.available is 0 ,

Could not download source data

this is my writePart code

public static void writePart(Part p) throws MessagingException, IOException {
        if (p.isMimeType("multipart/*")) {
            Multipart content = (Multipart) p.getContent();
            for (int i = 0; i < content.getCount(); i++) {
                writePart(content.getBodyPart(i));
            }

        }
        else if (p.isMimeType("text/*")) {
            System.out.println(p.getContent());
        }
        else if (p.isMimeType("image/jpeg")) {
            MimeBodyPart iPart = (MimeBodyPart) p;
            String imgName = iPart.getFileName();
            InputStream x = (InputStream) iPart.getContent();
            
            //here!!! x.available is zore,why
            System.out.println("x.length = " + x.available());
            int i = 0;
            byte[] bArray = new byte[x.available()];

            while ((i = x.available()) > 0) {
                int result = x.read(bArray);
                if (result == -1)
                    break;
            }
            //todo temp directory
            File file = new File("tmp/" + imgName);
            boolean b = file.getParentFile().mkdirs();
            FileOutputStream f2 = new FileOutputStream(file);
            f2.write(bArray);
        }
    }

Solution

  • This is not the correct way to read an InputStream. The documentation for InputStream.available states:

    Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. The next invocation might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.

    Note that while some implementations of InputStream will return the total number of bytes in the stream, many will not. It is never correct to use the return value of this method to allocate a buffer intended to hold all data in this stream.

    The key phrase there is "without blocking". This means there may be more data available on the remote side, so it's incorrect to use this to detect the end of the stream. Instead, you should check result (the return value from InputStream.read(bArray).

    It's also incorrect to use the result from available to allocate bArray. It is not possible in general to determine the size of all data that will be produced by an InputStream. In fact, regardless of the size of the buffer, it is not guaranteed that all of the data will be returned from the InputStream in a single call to read, so there needs to be a loop that either appends each read to a dynamically-sized buffer, or processes the read data in a streaming fashion. The latter is preferable, as it doesn't require retaining all of the data in memory at once, which could cause heap space exhaustion for large attachments in this case.

    Implementing low-level I/O operations correctly can be tricky, so it's better to use a high-level method when available. In the case of MimeBodyPart, you can use the writeTo method to automatically write to the FileOutputStream:

    iPart.writeTo(f2);
    

    This eliminates the need to explicitly get and read from the InputStream.