如何将整个输入流读到字节数组?


当前回答

您可以使用cactoos库提供可重用的面向对象的Java组件。 这个库强调OOP,因此没有静态方法、null等等,只有真实对象及其契约(接口)。 像读取InputStream这样的简单操作可以这样执行

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

使用专用类型Bytes来处理数据结构byte[]使我们能够使用OOP策略来解决手头的任务。 一些程序性的“效用”方法将禁止我们做的事情。 例如,您需要将从此InputStream读取的字节封装到Base64。 在本例中,您将使用Decorator模式并在Base64的实现中封装Bytes对象。 Cactoos已经提供了这样的实现:

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

您可以使用Decorator模式以同样的方式解码它们

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

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

其他回答

如果你使用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。

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
    }

你可以完全避免缓冲。

从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 9及更新版本: 最终字节[]字节; try (inputStream) { 字节= inputStream.readAllBytes(); }


Java 8 and older: 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(当Java 9+不可访问时): @Throws (IOException::类) fun InputStream.readAllBytes(): ByteArray { val bufLen = 4 * 0x400 // 4KB val buf = ByteArray(bufLen) var readLen: Int = 0 ByteArrayOutputStream()。使用{o -> 这一点。使用{I -> i.read(buf, 0, bufLen)。{readLen = it} != -1) o.write(buf, 0, readLen) } 返回o.toByteArray () } } 避免嵌套使用请看这里。


Scala(当Java 9+不可访问时)(By @Joan。Thx): def readAllBytes(inputStream: inputStream): Array[Byte] = Stream.continually (read)。takeWhile(_ != -1).map(_. tobyte).toArray . take (_

你真的需要图像作为字节[]吗?你在字节[]中到底期望什么-图像文件的完整内容,以图像文件的任何格式编码,或RGB像素值?

这里的其他答案向您展示了如何将文件读入字节[]。您的字节[]将包含文件的确切内容,并且您需要对其进行解码才能对图像数据进行处理。

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

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

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

查找javax.imageio的API文档。ImageIO java.awt.image。BufferedImage, java。awt。image。raster等等。

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

另请参阅下面的教程:使用图像