Determine the size of an InputStream

Viewed 267915

My current situation is: I have to read a file and put the contents into InputStream. Afterwards I need to place the contents of the InputStream into a byte array which requires (as far as I know) the size of the InputStream. Any ideas?

As requested, I will show the input stream that I am creating from an uploaded file

InputStream uploadedStream = null;
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
java.util.List items = upload.parseRequest(request);      
java.util.Iterator iter = items.iterator();

while (iter.hasNext()) {
    FileItem item = (FileItem) iter.next();
    if (!item.isFormField()) {
        uploadedStream = item.getInputStream();
        //CHANGE uploadedStreambyte = item.get()
    }
}

The request is a HttpServletRequest object, which is like the FileItemFactory and ServletFileUpload is from the Apache Commons FileUpload package.

13 Answers

I would read into a ByteArrayOutputStream and then call toByteArray() to get the resultant byte array. You don't need to define the size in advance (although it's possibly an optimisation if you know it. In many cases you won't)

I just wanted to add, Apache Commons IO has stream support utilities to perform the copy. (Btw, what do you mean by placing the file into an inputstream? Can you show us your code?)

Edit:

Okay, what do you want to do with the contents of the item? There is an item.get() which returns the entire thing in a byte array.

Edit2

item.getSize() will return the uploaded file size.

For InputStream

org.apache.commons.io.IoUtils.toByteArray(inputStream).length()

For Optional < MultipartFile >

Stream.of(multipartFile.get()).mapToLong(file->file.getSize()).findFirst().getAsLong()

The function below should work with any InputStream. As other answers have hinted, you can't reliably find the length of an InputStream without reading through it, but unlike other answers, you should not attempt to hold the entire stream in memory by reading into a ByteArrayOutputStream, nor is there any reason to. Instead of reading the stream, you should ideally rely on other API for stream sizes, for example getting the size of a file using the File API.

public static int length(InputStream inputStream, int chunkSize) throws IOException {
    byte[] buffer = new byte[chunkSize];
    int chunkBytesRead = 0;
    int length = 0;
    while((chunkBytesRead = inputStream.read(buffer)) != -1) {
        length += chunkBytesRead;
    }
    return length;
}

Choose a reasonable value for chunkSize appropriate to the kind of InputStream. E.g. reading from disk it would not be efficient to have too small a value for chunkSize.

When explicitly dealing with a ByteArrayInputStream then contrary to some of the comments on this page you can use the .available() function to get the size. Just have to do it before you start reading from it.

From the JavaDocs:

Returns the number of remaining bytes that can be read (or skipped over) from this input stream. The value returned is count - pos, which is the number of bytes remaining to be read from the input buffer.

https://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html#available()

If you know that your InputStream is a FileInputStream or a ByteArrayInputStream, you can use a little reflection to get at the stream size without reading the entire contents. Here's an example method:

static long getInputLength(InputStream inputStream) {
    try {
        if (inputStream instanceof FilterInputStream) {
            FilterInputStream filtered = (FilterInputStream)inputStream;
            Field field = FilterInputStream.class.getDeclaredField("in");
            field.setAccessible(true);
            InputStream internal = (InputStream) field.get(filtered);
            return getInputLength(internal);
        } else if (inputStream instanceof ByteArrayInputStream) {
            ByteArrayInputStream wrapper = (ByteArrayInputStream)inputStream;
            Field field = ByteArrayInputStream.class.getDeclaredField("buf");
            field.setAccessible(true);
            byte[] buffer = (byte[])field.get(wrapper);
            return buffer.length;
        } else if (inputStream instanceof FileInputStream) {
            FileInputStream fileStream = (FileInputStream)inputStream;
            return fileStream.getChannel().size();
        }
    } catch (NoSuchFieldException | IllegalAccessException | IOException exception) {
        // Ignore all errors and just return -1.
    }
    return -1;
}

This could be extended to support additional input streams, I am sure.

If you need to stream the data to another object that doesn't allow you to directly determine the size (e.g. javax.imageio.ImageIO), then you can wrap your InputStream within a CountingInputStream (Apache Commons IO), and then read the size:

CountingInputStream countingInputStream = new CountingInputStream(inputStream);
// ... process the whole stream ...
int size = countingInputStream.getCount();

Add to your pom.xml:

  <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
  </dependency>

Use to get the content type lenght (InputStream file):

IOUtils.toByteArray(file).length
Related