ChatGPT解决这个技术问题 Extra ChatGPT

在 Java 中将 InputStream 转换为字节数组

如何将整个 InputStream 读入字节数组?

在此处查看相反的内容: byte[] 到 InputStream:stackoverflow.com/questions/2091454/…

L
Lektonic

您可以使用 Apache Commons IO 来处理此任务和类似任务。

IOUtils 类型具有读取 InputStream 并返回 byte[] 的静态方法。

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

这在内部会创建一个 ByteArrayOutputStream 并将字节复制到输出,然后调用 toByteArray()。它通过复制 4KiB 块中的字节来处理大文件。


为了写 4 行代码,你认为导入 3rd-party 依赖值得吗?
如果有一个库可以处理需求,并处理大文件的处理,并且经过良好测试,那么问题肯定是我为什么要自己编写它? jar 只有 107KB,如果您需要其中的一种方法,您也可能会使用其他方法
@oxbow_lakes:考虑到我在开发人员生活中看到的该功能的错误实现数量之多,我觉得是的,外部依赖是非常值得的。
为什么不去看看像 FastArrayList 或他们的软 &弱参考 Maps 并回来告诉我这个库是如何“经过良好测试”的。这是一堆垃圾
除了 Apache commons-io,请查看 Google Guava 中的 ByteStreams 类。 InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
A
Arsen Khachaturyan

您需要从 InputStream 读取每个字节并将其写入 ByteArrayOutputStream

然后,您可以通过调用 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();

新创建的 byte[] 的大小呢?为什么是16384?我怎样才能确定完全正确的尺寸?非常感谢。
16384 是一个相当随意的选择,尽管我倾向于使用 2 的幂来增加数组与单词边界对齐的机会。 pihentagy 的回答显示了如何避免使用中间缓冲区,而是分配正确大小的数组。除非您正在处理大文件,否则我个人更喜欢上面的代码,它更优雅,可用于预先不知道要读取的字节数的 InputStreams。
@Adamski 创建的字节数组不会比您期望的数据在流中大得多,浪费内存吗?
@bluesm:是的,没错。但是,在我的示例中,字节数组只有 16Kb,按照今天的标准来说是如此之小。此外,当然,此内存将在之后再次被释放。
@Adamski 很多基础设施硬件、Web 服务器和操作系统层组件都在使用 4K 缓冲区来移动数据,所以这就是确切数字的原因,但主要的一点是,通过 4K 获得的性能提升很少它通常被认为是浪费内存。我假设这仍然是正确的,因为这是我十年前的知识!
H
Holger

最后,经过 20 年,有了一个不需要第三方库的简单解决方案,这要感谢 Java 9

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

另请注意解决重复需求的便捷方法 readNBytes(byte[] b, int off, int len)transferTo(OutputStream)


Java 文档 “请注意,此方法适用于方便将所有字节读入字节数组的简单情况。它不适用于读取具有大量数据的输入流。”事实上,我的测试仅限于 8192(未记录) 将其用于测试,但不在生产中。
@pdem 没有这样的限制。我刚刚通过将 2GiB 文件读入内存来验证它。只要可以分配适当大的数组,它就可以工作。如果你得到不同的结果,那是你的测试设置的问题。您不应该将如此大的文件读入内存,而是更喜欢在阅读时处理它们,这是完全不同的事情。它显然适用于问答中提出的所有解决方案,因为它们都是关于将整个文件读入内存的。
好吧,您非常被动,我已经测试了 2 个 jdk (11 17),它可以与您所说的大字节输入流一起使用,所以我猜可能 api 存在问题,它是一个 javamail:我从 mimemessage 内容中获取文件。奇怪的是,与javamail相关的错误,并没有出现在经典的手册阅读中。
这个特定的 InputStream 是否覆盖 readAllBytes()readNBytes
听起来像是值得单独提出一个问题。
G
Gerold Broser

使用 vanilla Java 的 DataInputStream 及其 readFully 方法(至少从 Java 1.4 开始存在):

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

这种方法还有其他一些风格,但我一直在这个用例中使用它。


+1 用于使用标准库而不是 3rd 方依赖项。不幸的是,它对我不起作用,因为我不知道预先流的长度。
@janus 它是一个“文件”。只有当您知道文件的长度或要读取的字节数时,这种方式才有效。
有趣的事情,但您必须知道要读取的(部分)流的确切长度。此外,类 DataInputStream 主要用于从流中读取主要类型(Longs、Shorts、Chars...),因此我们可以将这种用法视为对类的滥用。
如果您已经知道要从流中读取的数据的长度,那么这并不比 InputStream.read 好。
@LoganPickup InputStream.read 不保证返回您请求的所有字节!
J
Joachim Sauer

如果您碰巧使用了 Google Guava,它就像使用 ByteStreams 一样简单:

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

M
Mir-Ismaili

安全解决方案(正确关闭流):

Java 9+:最终字节 [] 字节;尝试 (inputStream) { 字节 = 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异常=空;尝试 { 尝试 (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) outputStream.write(buf, 0, readLen);返回 outputStream.toByteArray(); } } catch (IOException e) { 异常 = e;扔 e; } 最后 { if (exception == null) inputStream.close();否则尝试 { inputStream.close(); } catch (IOException e) { exception.addSuppressed(e); } } }

Kotlin(当 Java 9+ 无法访问时):@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() } } 为避免嵌套使用,请参见此处。

Scala(当 Java 9+ 无法访问时)(由 @Joan.Thx 提供):def readAllBytes(inputStream: InputStream): Array[Byte] = Stream.continually(inputStream.read).takeWhile(_ != -1)。地图(_.toByte).toArray


这是否意味着在某些时候您会使用双倍的内存,因为您同时拥有缓冲区和字节数组?没有办法将字节直接发送到输出字节数组吗?
@android开发者;对不起。我不知道答案!但我不这么认为。我认为这种方式(使用缓冲区)是一种优化方式。
我已经检查过了,但它似乎是您在不知道尺寸时可以选择的唯一解决方案。如果您已经知道大小,则可以直接创建具有给定大小的字节数组并填充它。因此,您使用一个函数来获取字节大小的参数,如果它有效,则使用它直接创建和填充字节数组,而无需创建任何其他大对象。
@android开发者;感谢你的信息。我不认识他们。
A
Arne Burmeister

与往常一样,Spring framework(自 3.2.2 以来的 spring-core)也为您提供了一些东西:StreamUtils.copyToByteArray()


像大多数其他人一样,我想避免将 3rd 方库用于如此简单的事情,但 Java 9 目前不是一个选项......幸运的是,我已经在使用 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();
}

这是一个例子,因此,简洁是当务之急。在某些情况下,在此处返回 null 也是正确的选择(尽管在生产环境中您也将拥有适当的异常处理和文档)。
我理解示例中的简洁性,但为什么不让示例方法抛出 IOException 而不是吞下它并返回一个无意义的值呢?
我冒昧地将'return null'更改为'throw IOException'
此处不需要 Try-with-resources,因为 ByteArrayOutputStream#close() 什么都不做。 (不需要 ByteArrayOutputStream#flush() 也什么都不做。)
h
harsh_v

万一有人仍在寻找没有依赖关系的解决方案,并且如果您有文件。

数据输入流

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

字节数组输出流

 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 raf = new RandomAccessFile(file, "r");
 byte[] data = new byte[(int) raf.length()];
 raf.readFully(data);

比如说,如果字节数组太大而导致堆OOM怎么办?是否有类似的解决方案将使用 JNI 来存储字节,然后我们可以从存储在那里的数据中使用 inputStream(某种临时缓存)?
抱歉,我不小心对这个答案投了反对票。您能否编辑一些字符以便我可以撤消单击?谢谢!
谢谢,@MichaelOuyang。我希望我的回答有帮助:)
J
Jesper

您真的需要将图像作为 byte[] 吗?您对 byte[] 的期望究竟是什么 - 图像文件的完整内容,以图像文件的任何格式编码,还是 RGB 像素值?

此处的其他答案向您展示了如何将文件读入 byte[]。您的 byte[] 将包含文件的确切内容,您需要对其进行解码才能对图像数据执行任何操作。

Java 用于读取(和写入)图像的标准 API 是 ImageIO API,您可以在包 javax.imageio 中找到它。您只需一行代码即可从文件中读取图像:

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

这将为您提供 BufferedImage,而不是 byte[]。要获取图像数据,您可以在 BufferedImage 上调用 getRaster()。这将为您提供一个 Raster 对象,该对象具有访问像素数据的方法(它具有多个 getPixel() / getPixels() 方法)。

查找 javax.imageio.ImageIOjava.awt.image.BufferedImagejava.awt.image.Raster 等的 API 文档。

ImageIO 默认支持多种图像格式:JPEG、PNG、BMP、WBMP 和 GIF。可以添加对更多格式的支持(您需要一个实现 ImageIO 服务提供者接口的插件)。

另请参阅以下教程:Working with Images


K
Kristian Kraljic

如果您不想使用 Apache commons-io 库,则此代码段取自 sun.misc.IOUtils 类。它几乎是使用 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;
}

这是一个有点奇怪的解决方案,长度是数组长度的上限。如果你知道长度,你只需要: byte[] output = new byte[length]; is.read(输出); (但请参阅我的回答)
@luke-hutchison 正如我所说,这是 sun.misc.IOUtils 的解决方案。在最常见的情况下,您预先不知道 InputStream 的大小,所以 if (length == -1) length = Integer.MAX_VALUE;适用。即使给定长度大于 InputStream 的长度,此解决方案也有效。
@LukeHutchison 如果你知道长度,你可以用几行来处理它。如果你看每个答案,每个人都在抱怨长度不知道。最后是一个标准的答案,可以与 Java 7 Android 一起使用,并且不需要任何外部库。
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:您可以完全避免缓冲。

http://www.exampledepot.com/egs/java.io/File2ByteArray.html 复制的代码(是的,它非常冗长,但需要的内存大小是其他解决方案的一半。)

// 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;
}

取决于预先知道大小。
当然,但他们应该知道大小:“我想读取图像”
如果您知道大小,那么 java 会为您提供代码。请参阅我的答案或谷歌“DataInputStream”,它是 readFully 方法。
如果 offset < bytes.lengthInputStream 将不会在引发该异常时关闭,则应添加 is.close()
那么更好的是,你应该使用 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();

但是,通常操作系统已经有足够的缓冲,因此对于较小的文件来说这不是一个大问题。这不像硬盘磁头会单独读取每个字节(硬盘是一个转动的玻璃板,上面有磁编码信息,有点像我们用来保存数据的那个奇怪的图标:P)。
@Maarten Bodewes:大多数设备都有一种块传输,所以并不是每个 read() 都会导致实际的设备访问,但是每个字节都有一个操作系统调用已经足以扼杀性能。虽然在该代码之前将 InputStream 包装在 BufferedInputStream 中会减少操作系统调用并显着减轻性能缺陷,但该代码仍会执行从一个缓冲区到另一个缓冲区的不必要的手动复制工作。
C
Christian Ullenboom

Java 9 最终会给你一个不错的方法:

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

这和单行的 InputStram.readAllBytes() 有什么区别?
肯定会在 ByteArrayOutputStream 中调整很多数组的大小,然后是数据的完整副本。
B
Bharathiraja S

在将 S3 对象转换为 ByteArray 时,我们看到一些 AWS 事务出现延迟。

注意:S3 对象是 PDF 文档(最大大小为 3 mb)。

我们使用选项 #1 (org.apache.commons.io.IOUtils) 将 S3 对象转换为 ByteArray。我们注意到 S3 提供了内置 IOUtils 方法来将 S3 对象转换为 ByteArray,我们要求您确认将 S3 对象转换为 ByteArray 的最佳方法是什么,以避免延迟。

选项1:

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

选项#2:

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

如果我们有任何其他更好的方法将 s3 对象转换为字节数组,请告诉我


S
Simple-Solution

我知道为时已晚,但我认为这是更清晰的解决方案,更具可读性......

/**
 * 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();
}

您应该使用 try-with-resources。
最后的整理需要在 finally 块中完成,以防出错,否则可能导致内存泄漏。
a
akostadinov

我试图通过编写垃圾数据的修复程序来编辑@numan 的答案,但编辑被拒绝。虽然这段简短的代码并不出色,但我看不到任何其他更好的答案。这对我来说最有意义:

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 不需要关闭。为便于阅读,省略了 try/finally 结构


D
Dominik Sandjaja

请参阅 InputStream.available() 文档:

尤其重要的是要意识到您不能使用此方法来调整容器的大小,并假设您可以读取整个流而不需要调整容器的大小。这样的调用者可能应该将他们读取的所有内容写入 ByteArrayOutputStream 并将其转换为字节数组。或者,如果您正在从文件中读取, File.length 返回文件的当前长度(尽管假设文件的长度不能改变可能是不正确的,读取文件本质上是活泼的)。


T
Tatarize

如果由于某种原因不在表格中,则将其包装在 DataInputStream 中,只需使用 read 对其进行锤击,直到它为您提供 -1 或您要求的整个块。

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 方式(感谢 BufferedReaderAdam 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>);
    }
}

请注意,此解决方案会擦除回车符 ('\r') 并且可能不合适。


那是 String。 OP 要求 byte[]
不仅仅是 \r 可能是个问题。此方法将字节转换为字符并再次转换回来(使用 InputStreamReader 的默认字符集)。在默认字符编码中无效的任何字节(例如,Linux 上的 UTF-8 为 -1)都将被损坏,甚至可能更改字节数。
似乎这是一个很好的答案,但面向文本。买家小心。
H
Huy Tower

另一种情况是在向服务器发送请求并等待响应后,通过流获取正确的字节数组。

/**
         * 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

如果您使用 ByteArrayOutputStream,您正在做一个额外的副本。如果你在开始读之前就知道了流的长度(比如InputStream实际上是一个FileInputStream,你可以在文件上调用file.length(),或者InputStream是一个zipfile入口InputStream,你可以调用zipEntry. length()),那么直接写入 byte[] 数组要好得多——它使用了一半的内存,并且节省了时间。

// 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);

注意,上面的最后一行处理在读取流时文件被截断,如果您需要处理这种可能性,但是如果在读取流时文件变长,则 byte[] 数组中的内容不会被延长为了包含新的文件内容,数组将被简单地截断为旧长度 inputStreamLength。


c
cchcc

我用这个。

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();
        }
    }

添加一些解释,说明此答案如何帮助 OP 解决当前问题
D
Daniel De León

这是我的复制粘贴版本:

@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();
}

虽然此代码段可能会解决问题,但 including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。
A
Antonio

Java 7 及更高版本:

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

sun.misc.IOUtils 不是“Java 7”。它是一个专有的、特定于实现的类,可能不存在于其他 JRE 实现中,并且可以在下一个版本中消失而没有任何警告。
y
yegor256

您可以尝试 Cactoos

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

C
Christian d'Heureuse

这是一个优化版本,它尽量避免复制数据字节:

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

Kotlin 中的解决方案(当然也可以在 Java 中使用),其中包括您是否知道大小的两种情况:

    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
        }
    }

如果您知道大小,与其他解决方案相比,它可以节省两倍的内存使用量(在短时间内,但仍然可能有用)。那是因为您必须将整个流读取到最后,然后将其转换为字节数组(类似于 ArrayList 将其转换为数组)。

因此,例如,如果您使用的是 Android,并且需要处理一些 Uri,则可以尝试使用以下方法获取大小:

    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

您可以将 cactoos 库与提供可重用的 object-oriented Java 组件一起使用。该库强调 OOP,因此没有静态方法、NULL 等,只有 real objects 及其合约(接口)。像读取InputStream这样简单的操作,可以这样进行

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

拥有用于处理数据结构 byte[] 的专用类型 Bytes 使我们能够使用 OOP 策略来解决手头的任务。程序“实用”方法将禁止我们做的事情。例如,您需要将从 InputStream 读取的字节编码为 Base64。在这种情况下,您将使用 Decorator pattern 并将 Bytes 对象包装在 Base64 的实现中。 cactoos 已经提供了这样的实现:

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

您可以使用装饰器模式以相同的方式对它们进行解码

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

无论您的任务是什么,您都可以创建自己的 Bytes 实现来解决它。