ChatGPT解决这个技术问题 Extra ChatGPT

Convert InputStream to byte array in Java

How do I read an entire InputStream into a byte array?

See the reverse: byte[] to InputStream here: stackoverflow.com/questions/2091454/…

L
Lektonic

You can use Apache Commons IO to handle this and similar tasks.

The IOUtils type has a static method to read an InputStream and return a byte[].

InputStream is;
byte[] bytes = IOUtils.toByteArray(is);

Internally this creates a ByteArrayOutputStream and copies the bytes to the output, then calls toByteArray(). It handles large files by copying the bytes in blocks of 4KiB.


For the want of writing 4 lines of code, you think that importing a 3rd-party dependency is worthwhile?
If there is a library that handles the requirement, and deals with processing for large files, and is well tested, surely the question is why would I write it myself? The jar is only 107KB and if you have need for one method from it, you are likely to use others too
@oxbow_lakes: considering the staggering amount of wrong implementations of this feature I've seen in my developer life, I feel that yes it's very much worth the external dependency to get it right.
Why not go and have a look at Apache commons stuff like FastArrayList or their soft & weak reference Maps and come back to tell me how "well-tested" this library is. It's a pile of rubbish
In addition to Apache commons-io, check out the ByteStreams class from Google Guava. InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
A
Arsen Khachaturyan

You need to read each byte from your InputStream and write it to a ByteArrayOutputStream.

You can then retrieve the underlying byte array by calling toByteArray():

InputStream is = ...
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[16384];

while ((nRead = is.read(data, 0, data.length)) != -1) {
  buffer.write(data, 0, nRead);
}

return buffer.toByteArray();

What about the size of newly created byte[]. Why it is 16384? How could I determine exact right size? Thank you very much.
16384 is a fairly arbitrary choice although I tend to favour powers of 2 to increase the chance of the array aligning with word boundaries. pihentagy's answer shows how you can avoid using an intermediate buffer, but rather allocate an array of the correct size. Unless you're dealing with large files I personally prefer the code above, which is more elegant and can be used for InputStreams where the number of bytes to read is not known in advance.
@Adamski Isn't creating array of bytes lot bigger than you expect data would be in the stream, waste the memory ?
@bluesm: Yes that's correct. However, in my example the byte array is only 16Kb and so tiny by today's standards. Also, of course this memory will be freed again afterwards.
@Adamski A lot of infrastructure hardware, web-servers, and OS-layer components are using 4K buffers to move data, so that's the reason for the exact number, but the main point is that you get such little performance boost by going over 4K that it's generally considered wasteful of memory. I'm assuming this is still true, because it's decade old knowledge I had!
H
Holger

Finally, after twenty years, there’s a simple solution without the need for a 3rd party library, thanks to Java 9:

InputStream is;
…
byte[] array = is.readAllBytes();

Note also the convenience methods readNBytes(byte[] b, int off, int len) and transferTo(OutputStream) addressing recurring needs.


Java documentation "Note that this method is intended for simple cases where it is convenient to read all bytes into a byte array. It is not intended for reading input streams with large amounts of data." and in fact it was limited to 8192 from my test (undocumented) Use it for test, but not in production.
@pdem there is no such limit. I just verified it by reading a 2GiB file into memory. It works, as long as an appropriately large array can be allocated. If you get different results, it’s a problem of your test setup. The point that you should not read such large files into memory, but prefer processing them while reading, is an entirely different thing. It obviously applies to all solutions presented in the Q&A, as they all are about reading an entire file into memory.
Well you are very reactive, I've tested 2 jdk (11 17) and it works with a big byteInputstream as you said, so i Guess there is a problem with may api with is a javamail: I get files from a mimemessage content. What is strange is that the bug related to the javamail, doesn't appear with a classical manual read.
Does this specific InputStream override readAllBytes() or readNBytes?
Sounds like being worth a question on its own.
G
Gerold Broser

Use vanilla Java's DataInputStream and its readFully Method (exists since at least Java 1.4):

...
byte[] bytes = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
dis.readFully(bytes);
...

There are some other flavors of this method, but I use this all the time for this use case.


+1 for using the standard libraries instead of a 3rd party dependency. Unfortunately it doesn't work for me because I don't know the length of the stream upfront.
@janus it is a "File". this way only works if u know the length of the file or the count of bytes to read.
Interesting thing, but you must know the exact length of the (part of the) stream to read. Moreover, the class DataInputStream is primary used to read primary types (Longs, Shorts, Chars...) from a stream, so we can see this usage as a misuse of the class.
If you already know the length of the data to read from the stream, this is no better than InputStream.read.
@LoganPickup InputStream.read does not guarantee to return all the bytes you request!
J
Joachim Sauer

If you happen to use Google Guava, it'll be as simple as using ByteStreams:

byte[] bytes = ByteStreams.toByteArray(inputStream);

M
Mir-Ismaili

Safe solution (close streams correctly):

Java 9+: final byte[] bytes; try (inputStream) { bytes = inputStream.readAllBytes(); }

Java 8: public static byte[] readAllBytes(InputStream inputStream) throws IOException { final int bufLen = 4 * 0x400; // 4KB byte[] buf = new byte[bufLen]; int readLen; IOException exception = null; try { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) outputStream.write(buf, 0, readLen); return outputStream.toByteArray(); } } catch (IOException e) { exception = e; throw e; } finally { if (exception == null) inputStream.close(); else try { inputStream.close(); } catch (IOException e) { exception.addSuppressed(e); } } }

Kotlin (when Java 9+ isn't accessible): @Throws(IOException::class) fun InputStream.readAllBytes(): ByteArray { val bufLen = 4 * 0x400 // 4KB val buf = ByteArray(bufLen) var readLen: Int = 0 ByteArrayOutputStream().use { o -> this.use { i -> while (i.read(buf, 0, bufLen).also { readLen = it } != -1) o.write(buf, 0, readLen) } return o.toByteArray() } } To avoid nested use see here.

Scala (when Java 9+ isn't accessible) (By @Joan. Thx): def readAllBytes(inputStream: InputStream): Array[Byte] = Stream.continually(inputStream.read).takeWhile(_ != -1).map(_.toByte).toArray


Doesn't it mean that at some point you'd have double the memory used, because you have both the buffer and the byte array? Isn't there a way to send the bytes directly to the output byte array?
@androiddeveloper; I'm sorry. I don't know the answer! But I don't think so. I think this way (using buffer) is an optimized way.
I've checked and it does, but it seems that it's the only solution you can choose when you don't know the size. If you already know the size, you can directly create the byte-array with the given size and fill it. So, you use a function that will get a parameter of the byte size, and if it's valid, use it to directly create and fill the byte array, without creating any other large object.
@androiddeveloper; Thanks for your information. I didn't know them.
A
Arne Burmeister

As always, also Spring framework (spring-core since 3.2.2) has something for you: StreamUtils.copyToByteArray()


Like most others, I wanted to avoid using a 3rd party library for something so simple, but Java 9 is not an option at the moment...luckily, I was already using Spring.
I
Ivan Gammel
public static byte[] getBytesFromInputStream(InputStream is) throws IOException {
    ByteArrayOutputStream os = new ByteArrayOutputStream(); 
    byte[] buffer = new byte[0xFFFF];
    for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { 
        os.write(buffer, 0, len);
    }
    return os.toByteArray();
}

It is an example and as such, brevity is the order of the day. Also returning null here would be the proper choice in some cases (although in a production environment you would also have proper exception handling and documentation).
I understand brevity in an example, but why not just make the example method throw IOException rather than swallowing it and returning a meaningless value?
i've taken the liberty to change from 'return null' to 'throw IOException'
Try-with-resources is not needed here, because ByteArrayOutputStream#close() does nothing. (ByteArrayOutputStream#flush() is not needed and does nothing too.)
h
harsh_v

In-case someone is still looking for a solution without dependency and If you have a file.

DataInputStream

 byte[] data = new byte[(int) file.length()];
 DataInputStream dis = new DataInputStream(new FileInputStream(file));
 dis.readFully(data);
 dis.close();

ByteArrayOutputStream

 InputStream is = new FileInputStream(file);
 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 int nRead;
 byte[] data = new byte[(int) file.length()];
 while ((nRead = is.read(data, 0, data.length)) != -1) {
     buffer.write(data, 0, nRead);
 }

RandomAccessFile

 RandomAccessFile raf = new RandomAccessFile(file, "r");
 byte[] data = new byte[(int) raf.length()];
 raf.readFully(data);

Say, what if the byte array is too large which could cause OOM for the heap? Is there a similar solution that will use JNI to store the bytes, and later we'd be able to use inputStream from the data stored there (sort of temporary cache) ?
Sorry, I accidentally downvoted this answer. Could you please edit some characters so that i can undo the click? thanks!
Thanks, @MichaelOuyang. I hope my answer was of help :)
J
Jesper

Do you really need the image as a byte[]? What exactly do you expect in the byte[] - the complete content of an image file, encoded in whatever format the image file is in, or RGB pixel values?

Other answers here show you how to read a file into a byte[]. Your byte[] will contain the exact contents of the file, and you'd need to decode that to do anything with the image data.

Java's standard API for reading (and writing) images is the ImageIO API, which you can find in the package javax.imageio. You can read in an image from a file with just a single line of code:

BufferedImage image = ImageIO.read(new File("image.jpg"));

This will give you a BufferedImage, not a byte[]. To get at the image data, you can call getRaster() on the BufferedImage. This will give you a Raster object, which has methods to access the pixel data (it has several getPixel() / getPixels() methods).

Lookup the API documentation for javax.imageio.ImageIO, java.awt.image.BufferedImage, java.awt.image.Raster etc.

ImageIO supports a number of image formats by default: JPEG, PNG, BMP, WBMP and GIF. It's possible to add support for more formats (you'd need a plug-in that implements the ImageIO service provider interface).

See also the following tutorial: Working with Images


K
Kristian Kraljic

If you don't want to use the Apache commons-io library, this snippet is taken from the sun.misc.IOUtils class. It's nearly twice as fast as the common implementation using ByteBuffers:

public static byte[] readFully(InputStream is, int length, boolean readAll)
        throws IOException {
    byte[] output = {};
    if (length == -1) length = Integer.MAX_VALUE;
    int pos = 0;
    while (pos < length) {
        int bytesToRead;
        if (pos >= output.length) { // Only expand when there's no room
            bytesToRead = Math.min(length - pos, output.length + 1024);
            if (output.length < pos + bytesToRead) {
                output = Arrays.copyOf(output, pos + bytesToRead);
            }
        } else {
            bytesToRead = output.length - pos;
        }
        int cc = is.read(output, pos, bytesToRead);
        if (cc < 0) {
            if (readAll && length != Integer.MAX_VALUE) {
                throw new EOFException("Detect premature EOF");
            } else {
                if (output.length != pos) {
                    output = Arrays.copyOf(output, pos);
                }
                break;
            }
        }
        pos += cc;
    }
    return output;
}

This is a bit of a weird solution, length is an upper bound on the length of the array. If you know the length, all you need is: byte[] output = new byte[length]; is.read(output); (but see my answer)
@luke-hutchison as I said, this is the solution of sun.misc.IOUtils. In the most common cases you don't know the size of an InputStream upfront, so if (length == -1) length = Integer.MAX_VALUE; applies. This solution works, even if the given length is larger than the length of the InputStream.
@LukeHutchison If you know the length you can handle it with a few lines. If you look at each answer, everyone is complaining that the length is not known. Finally an answer which is standard, can be used with Java 7 Android, and doesn't require any external library.
Y
YulCheney
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (true) {
    int r = in.read(buffer);
    if (r == -1) break;
    out.write(buffer, 0, r);
}

byte[] ret = out.toByteArray();

p
pihentagy

@Adamski: You can avoid buffer entirely.

Code copied from http://www.exampledepot.com/egs/java.io/File2ByteArray.html (Yes, it is very verbose, but needs half the size of memory as the other solution.)

// Returns the contents of the file in a byte array.
public static byte[] getBytesFromFile(File file) throws IOException {
    InputStream is = new FileInputStream(file);

    // Get the size of the file
    long length = file.length();

    // You cannot create an array using a long type.
    // It needs to be an int type.
    // Before converting to an int type, check
    // to ensure that file is not larger than Integer.MAX_VALUE.
    if (length > Integer.MAX_VALUE) {
        // File is too large
    }

    // Create the byte array to hold the data
    byte[] bytes = new byte[(int)length];

    // Read in the bytes
    int offset = 0;
    int numRead = 0;
    while (offset < bytes.length
           && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
        offset += numRead;
    }

    // Ensure all the bytes have been read in
    if (offset < bytes.length) {
        throw new IOException("Could not completely read file "+file.getName());
    }

    // Close the input stream and return bytes
    is.close();
    return bytes;
}

Depends on knowing size upfront.
Of course, but they should know the size: "I want to read an image"
if you know the size, then java provides the code for you. see my answer or google for "DataInputStream" and it's readFully method.
You should add is.close() if offset < bytes.length or the InputStream will not be closed if that exception is thrown.
Then better, you should use try-with-resources
A
Aturio
Input Stream is ...
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int next = in.read();
while (next > -1) {
    bos.write(next);
    next = in.read();
}
bos.flush();
byte[] result = bos.toByteArray();
bos.close();

However, usually the OS already buffers enough for this not be be a huge worry for smaller files. It's not like the hard disk head will read each byte separately (a hard disk is a turning glass plate with magnetic coded information on it, a bit like that weird icon we use for saving data :P).
@Maarten Bodewes: most devices have a kind of block transfer, so not every read() will cause an actual device access, indeed, but having an OS-call per byte is already sufficient to kill the performance. While wrapping the InputStream in a BufferedInputStream before that code would reduce the OS-calls and mitigate the performance drawbacks significantly, that code will still do unnecessary manual copying work from one buffer to another.
C
Christian Ullenboom

Java 9 will give you finally a nice method:

InputStream in = ...;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
in.transferTo( bos );
byte[] bytes = bos.toByteArray();

What's the difference between this and InputStram.readAllBytes() that is one-liner?
A lot of array resizing in ByteArrayOutputStream for sure, followed by a full copy of the data.
B
Bharathiraja S

We are seeing some delay for few AWS transaction, while converting S3 object to ByteArray.

Note: S3 Object is PDF document (max size is 3 mb).

We are using the option #1 (org.apache.commons.io.IOUtils) to convert the S3 object to ByteArray. We have noticed S3 provide the inbuild IOUtils method to convert the S3 object to ByteArray, we are request you to confirm what is the best way to convert the S3 object to ByteArray to avoid the delay.

Option #1:

import org.apache.commons.io.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

Option #2:

import com.amazonaws.util.IOUtils;
is = s3object.getObjectContent();
content =IOUtils.toByteArray(is);

Also let me know if we have any other better way to convert the s3 object to bytearray


S
Simple-Solution

I know it's too late but here I think is cleaner solution that's more readable...

/**
 * method converts {@link InputStream} Object into byte[] array.
 * 
 * @param stream the {@link InputStream} Object.
 * @return the byte[] array representation of received {@link InputStream} Object.
 * @throws IOException if an error occurs.
 */
public static byte[] streamToByteArray(InputStream stream) throws IOException {

    byte[] buffer = new byte[1024];
    ByteArrayOutputStream os = new ByteArrayOutputStream();

    int line = 0;
    // read bytes from stream, and store them in buffer
    while ((line = stream.read(buffer)) != -1) {
        // Writes bytes from byte array (buffer) into output stream.
        os.write(buffer, 0, line);
    }
    stream.close();
    os.flush();
    os.close();
    return os.toByteArray();
}

You should use try-with-resources.
Your tidying up at the end needs to be done in a finally block in case of errors, otherwise this could cause a memory leak.
a
akostadinov

I tried to edit @numan's answer with a fix for writing garbage data but edit was rejected. While this short piece of code is nothing brilliant I can't see any other better answer. Here's what makes most sense to me:

ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024]; // you can configure the buffer size
int length;

while ((length = in.read(buffer)) != -1) out.write(buffer, 0, length); //copy streams
in.close(); // call this in a finally block

byte[] result = out.toByteArray();

btw ByteArrayOutputStream need not be closed. try/finally constructs omitted for readability


D
Dominik Sandjaja

See the InputStream.available() documentation:

It is particularly important to realize that you must not use this method to size a container and assume that you can read the entirety of the stream without needing to resize the container. Such callers should probably write everything they read to a ByteArrayOutputStream and convert that to a byte array. Alternatively, if you're reading from a file, File.length returns the current length of the file (though assuming the file's length can't change may be incorrect, reading a file is inherently racy).


T
Tatarize

Wrap it in a DataInputStream if that is off the table for some reason, just use read to hammer on it until it gives you a -1 or the entire block you asked for.

public int readFully(InputStream in, byte[] data) throws IOException {
    int offset = 0;
    int bytesRead;
    boolean read = false;
    while ((bytesRead = in.read(data, offset, data.length - offset)) != -1) {
        read = true;
        offset += bytesRead;
        if (offset >= data.length) {
            break;
        }
    }
    return (read) ? offset : -1;
}

y
yegor256

Java 8 way (thanks to BufferedReader and Adam Bien)

private static byte[] readFully(InputStream input) throws IOException {
    try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
        return buffer.lines().collect(Collectors.joining("\n")).getBytes(<charset_can_be_specified>);
    }
}

Note that this solution wipes carriage return ('\r') and can be inappropriate.


That is for String. OP is asking for byte[].
It's not just \r that could be a problem. This method converts the bytes to characters and back again (using the default character set for InputStreamReader). Any bytes which aren't valid in the default character encoding (say, -1 for UTF-8 on Linux) will be corrupted, potentially even changing the number of bytes.
Seems like this is good answer, but text-oriented. Buyer beware.
H
Huy Tower

The other case to get correct byte array via stream, after send request to server and waiting for the response.

/**
         * Begin setup TCP connection to PC app
         * to open integrate connection between mobile app and pc app (or mobile app)
         */
        mSocket = new Socket(IP, port);
       // mSocket.setSoTimeout(30000);

        DataOutputStream mDos = new DataOutputStream(mSocket.getOutputStream());

        String str = "MobileRequest#" + params[0] + "#<EOF>";

        mDos.write(str.getBytes());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        /* Since data are accepted as byte, all of them will be collected in the
        following byte array which initialised with accepted data length. */
        DataInputStream mDis = new DataInputStream(mSocket.getInputStream());
        byte[] data = new byte[mDis.available()];

        // Collecting data into byte array
        for (int i = 0; i < data.length; i++)
            data[i] = mDis.readByte();

        // Converting collected data in byte array into String.
        String RESPONSE = new String(data);

L
Luke Hutchison

You're doing an extra copy if you use ByteArrayOutputStream. If you know the length of the stream before you start reading it (e.g. the InputStream is actually a FileInputStream, and you can call file.length() on the file, or the InputStream is a zipfile entry InputStream, and you can call zipEntry.length()), then it's far better to write directly into the byte[] array -- it uses half the memory, and saves time.

// Read the file contents into a byte[] array
byte[] buf = new byte[inputStreamLength];
int bytesRead = Math.max(0, inputStream.read(buf));

// If needed: for safety, truncate the array if the file may somehow get
// truncated during the read operation
byte[] contents = bytesRead == inputStreamLength ? buf
                  : Arrays.copyOf(buf, bytesRead);

N.B. the last line above deals with files getting truncated while the stream is being read, if you need to handle that possibility, but if the file gets longer while the stream is being read, the contents in the byte[] array will not be lengthened to include the new file content, the array will simply be truncated to the old length inputStreamLength.


c
cchcc

I use this.

public static byte[] toByteArray(InputStream is) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        try {
            byte[] b = new byte[4096];
            int n = 0;
            while ((n = is.read(b)) != -1) {
                output.write(b, 0, n);
            }
            return output.toByteArray();
        } finally {
            output.close();
        }
    }

Add some explanation with answer for how this answer help OP in fixing current issue
D
Daniel De León

This is my copy-paste version:

@SuppressWarnings("empty-statement")
public static byte[] inputStreamToByte(InputStream is) throws IOException {
    if (is == null) {
        return null;
    }
    // Define a size if you have an idea of it.
    ByteArrayOutputStream r = new ByteArrayOutputStream(2048);
    byte[] read = new byte[512]; // Your buffer size.
    for (int i; -1 != (i = is.read(read)); r.write(read, 0, i));
    is.close();
    return r.toByteArray();
}

While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.
A
Antonio

Java 7 and later:

import sun.misc.IOUtils;
...
InputStream in = ...;
byte[] buf = IOUtils.readFully(in, -1, false);

sun.misc.IOUtils is not “Java 7”. It’s a proprietary, implementation specific class that may not be present in other JRE implementations and can disappear without any warning in one of the next releases.
y
yegor256

You can try Cactoos:

byte[] array = new BytesOf(stream).bytes();

C
Christian d'Heureuse

Here is an optimized version, that tries to avoid copying data bytes as much as possible:

private static byte[] loadStream (InputStream stream) throws IOException {
   int available = stream.available();
   int expectedSize = available > 0 ? available : -1;
   return loadStream(stream, expectedSize);
}

private static byte[] loadStream (InputStream stream, int expectedSize) throws IOException {
   int basicBufferSize = 0x4000;
   int initialBufferSize = (expectedSize >= 0) ? expectedSize : basicBufferSize;
   byte[] buf = new byte[initialBufferSize];
   int pos = 0;
   while (true) {
      if (pos == buf.length) {
         int readAhead = -1;
         if (pos == expectedSize) {
            readAhead = stream.read();       // test whether EOF is at expectedSize
            if (readAhead == -1) {
               return buf;
            }
         }
         int newBufferSize = Math.max(2 * buf.length, basicBufferSize);
         buf = Arrays.copyOf(buf, newBufferSize);
         if (readAhead != -1) {
            buf[pos++] = (byte)readAhead;
         }
      }
      int len = stream.read(buf, pos, buf.length - pos);
      if (len < 0) {
         return Arrays.copyOf(buf, pos);
      }
      pos += len;
   }
}

a
android developer

Solution in Kotlin (will work in Java too, of course), which includes both cases of when you know the size or not:

    fun InputStream.readBytesWithSize(size: Long): ByteArray? {
        return when {
            size < 0L -> this.readBytes()
            size == 0L -> ByteArray(0)
            size > Int.MAX_VALUE -> null
            else -> {
                val sizeInt = size.toInt()
                val result = ByteArray(sizeInt)
                readBytesIntoByteArray(result, sizeInt)
                result
            }
        }
    }

    fun InputStream.readBytesIntoByteArray(byteArray: ByteArray,bytesToRead:Int=byteArray.size) {
        var offset = 0
        while (true) {
            val read = this.read(byteArray, offset, bytesToRead - offset)
            if (read == -1)
                break
            offset += read
            if (offset >= bytesToRead)
                break
        }
    }

If you know the size, it saves you on having double the memory used compared to the other solutions (in a brief moment, but still could be useful). That's because you have to read the entire stream to the end, and then convert it to a byte array (similar to ArrayList which you convert to just an array).

So, if you are on Android, for example, and you got some Uri to handle, you can try to get the size using this:

    fun getStreamLengthFromUri(context: Context, uri: Uri): Long {
        context.contentResolver.query(uri, arrayOf(MediaStore.MediaColumns.SIZE), null, null, null)?.use {
            if (!it.moveToNext())
                return@use
            val fileSize = it.getLong(it.getColumnIndex(MediaStore.MediaColumns.SIZE))
            if (fileSize > 0)
                return fileSize
        }
        //if you wish, you can also get the file-path from the uri here, and then try to get its size, using this: https://stackoverflow.com/a/61835665/878126
        FileUtilEx.getFilePathFromUri(context, uri, false)?.use {
            val file = it.file
            val fileSize = file.length()
            if (fileSize > 0)
                return fileSize
        }
        context.contentResolver.openInputStream(uri)?.use { inputStream ->
            if (inputStream is FileInputStream)
                return inputStream.channel.size()
            else {
                var bytesCount = 0L
                while (true) {
                    val available = inputStream.available()
                    if (available == 0)
                        break
                    val skip = inputStream.skip(available.toLong())
                    if (skip < 0)
                        break
                    bytesCount += skip
                }
                if (bytesCount > 0L)
                    return bytesCount
            }
        }
        return -1L
    }

a
andreoss

You can use cactoos library with provides reusable object-oriented Java components. OOP is emphasized by this library, so no static methods, NULLs, and so on, only real objects and their contracts (interfaces). A simple operation like reading InputStream, can be performed like that

final InputStream input = ...;
final Bytes bytes = new BytesOf(input);
final byte[] array = bytes.asBytes();
Assert.assertArrayEquals(
    array,
    new byte[]{65, 66, 67}
);

Having a dedicated type Bytes for working with data structure byte[] enables us to use OOP tactics for solving tasks at hand. Something that a procedural "utility" method will forbid us to do. For example, you need to enconde bytes you've read from this InputStream to Base64. In this case you will use Decorator pattern and wrap Bytes object within implementation for Base64. cactoos already provides such implementation:

final Bytes encoded = new BytesBase64(
    new BytesOf(
        new InputStreamOf("XYZ")
    )
);
Assert.assertEquals(new TextOf(encoded).asString(), "WFla");

You can decode them in the same manner, by using Decorator pattern

final Bytes decoded = new Base64Bytes(
    new BytesBase64(
        new BytesOf(
            new InputStreamOf("XYZ")
        )
    )
);
Assert.assertEquals(new TextOf(decoded).asString(), "XYZ");

Whatever your task is you will be able to create own implementation of Bytes to solve it.