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


当前回答

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

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

我们使用选项#1将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对象到bytearray

其他回答

我知道已经太迟了,但我认为这里有更清晰的解决方案,更易于阅读……

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

我用这个。

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

这是我的复制粘贴版本:

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

您可以使用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实现来解决它。

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
    }