Converter InputStream em matriz de bytes em Java


Respostas:


1136

Você pode usar o Apache Commons IO para lidar com essa e outras tarefas semelhantes.

O IOUtilstipo possui um método estático para ler InputStreame retornar a byte[].

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

Internamente, isso cria ae ByteArrayOutputStreamcopia os bytes para a saída e depois chama toByteArray(). Ele lida com arquivos grandes, copiando os bytes em blocos de 4KiB.


188
Por falta de escrever 4 linhas de código, você acha que vale a pena importar uma dependência de terceiros?
Oxbow_lakes 12/08/09

217
Se existe uma biblioteca que lida com o requisito, lida com o processamento de arquivos grandes e é bem testada, certamente a pergunta é por que eu mesmo a escreveria? O jar tem apenas 107 KB e, se você precisar de um método, provavelmente usará outros também.
Rich Seller

242
@oxbow_lakes: considerando a quantidade impressionante de errado implementações deste recurso que já vi em minha vida desenvolvedor, eu sinto que sim , é muito muito valor a dependência externa de acertar.
Joachim Sauer

17
Por que não dar uma olhada no Apache commons, como em FastArrayListseus mapas de referência suaves e fracos, e voltar para me dizer como esta biblioteca é "bem testada". É uma pilha de lixo
oxbow_lakes

87
Além do Apache commons-io, confira a classe ByteStreams no Google Guava . InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
michaelok

446

Você precisa ler cada byte do seu InputStreame gravá-lo em a ByteArrayOutputStream.

Você pode recuperar a matriz de bytes subjacente chamando 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();

17
E o tamanho do byte recém-criado []. Por que é 16384? Como posso determinar o tamanho exato exato? Muito obrigado.
Ondrej Bozek

6
16384 é uma escolha bastante arbitrária, embora eu tenha tendência a favorecer potências de 2 para aumentar a chance da matriz se alinhar com os limites das palavras. A resposta da pihentagy mostra como você pode evitar o uso de um buffer intermediário, mas alocar uma matriz do tamanho correto. A menos que você esteja lidando com arquivos grandes, eu pessoalmente prefiro o código acima, que é mais elegante e pode ser usado para InputStreams em que o número de bytes a serem lidos não é conhecido antecipadamente.
Adamski

@Adamski A criação de uma matriz de bytes não é muito maior do que você espera que os dados estejam no fluxo, desperdiça a memória?
Paul Brewczynski

@ bluesm: Sim, está correto. No entanto, no meu exemplo, a matriz de bytes é de apenas 16 KB e é minúscula para os padrões atuais. Além disso, é claro que essa memória será liberada novamente posteriormente.
Adamski

5
@Adamski Um monte de hardware de infraestrutura, servidores da web e componentes da camada do sistema operacional estão usando buffers de 4K para mover dados, então esse é o motivo do número exato, mas o ponto principal é que você obtém tão pouco aumento de desempenho ao passar por 4K que geralmente é considerado um desperdício de memória. Eu estou supondo que isso ainda é verdade, porque é o conhecimento que tenho há uma década!


132

Use baunilha Java DataInputStreame seu readFullymétodo (existe desde pelo menos Java 1.4):

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

Existem outros tipos desse método, mas eu uso isso o tempo todo para este caso de uso.


45
+1 por usar as bibliotecas padrão em vez de uma dependência de terceiros. Infelizmente, isso não funciona para mim porque não sei a duração do fluxo antecipadamente.
Andrew Spencer

2
o que é imgFile? Não pode ser um InputStream, que deveria ser a entrada desse método
Janus Troelsen

4
@janus é um "arquivo". dessa forma, só funciona se você souber o comprimento do arquivo ou a contagem de bytes a serem lidos.
Dermoritz

5
Interessante, mas você deve saber o comprimento exato do (parte do) fluxo para ler. Além disso, a classe DataInputStreamé usada principalmente para ler tipos primários (Longs, Shorts, Chars ...) de um fluxo, para que possamos ver esse uso como um uso indevido da classe.
Olivier Faucheux

17
Se você já sabe o tamanho dos dados a serem lidos do fluxo, isso não é melhor que InputStream.read.
Logan Pickup

119

Se você usar google goiaba , será tão simples quanto:

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

8
ByteStreamsé anotado com@Beta
Kid101

46

Como sempre, também o framework Spring (core desde 3.2.2) tem algo para você:StreamUtils.copyToByteArray()


Como a maioria dos outros, eu queria evitar o uso de uma biblioteca de terceiros para algo tão simples, mas o Java 9 não é uma opção no momento ... felizmente, eu já estava usando o Spring.
scottysseus

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

2
É um exemplo e, como tal, a brevidade está na ordem do dia. Também retornar nulo aqui seria a escolha adequada em alguns casos (embora em um ambiente de produção você também tenha manipulação e documentação de exceção).

11
Entendo a brevidade em um exemplo, mas por que não fazer o método de exemplo lançar IOException em vez de engoli-lo e retornar um valor sem sentido?
Pendor

4
Tomei a liberdade de mudar de 'null retorno' para 'jogar IOException'
kritzikratzi

3
A tentativa com recursos não é necessária aqui, porque ByteArrayOutputStream # close () não faz nada. (ByteArrayOutputStream # flush () não é necessário e também não faz nada.)
Luke Hutchison

25

Solução segura (com capacidade declosefluxos corretamente):

  • Versão Java 9+:

    final byte[] bytes;
    try (inputStream) {
        bytes = inputStream.readAllBytes();
    }
  • Versão 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);
            }
        }
    }
  • Versão Kotlin (quando o Java 9+ não está acessível):

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

    Para evitar aninhados, useveja aqui .


Isso não significa que, em algum momento, você teria o dobro da memória usada, porque possui o buffer e a matriz de bytes? Não existe uma maneira de enviar os bytes diretamente para a matriz de bytes de saída?
desenvolvedor android

@androiddeveloper; Eu sinto Muito. Eu não sei a resposta! Mas acho que não. Eu acho que desta maneira (usando buffer) é uma maneira otimizada.
Mir-Ismaili

Eu verifiquei e sim, mas parece que é a única solução que você pode escolher quando não sabe o tamanho. Se você já conhece o tamanho, pode criar diretamente a matriz de bytes com o tamanho especificado e preenchê-lo. Portanto, você usa uma função que obterá um parâmetro do tamanho de bytes e, se for válido, use-a para criar e preencher diretamente a matriz de bytes, sem criar nenhum outro objeto grande.
desenvolvedor android

@androiddeveloper; Obrigado pela sua informação. Eu não os conhecia.
Mir-Ismaili

19

Você realmente precisa da imagem como um byte[]? O que exatamente você espera nobyte[] - o conteúdo completo de um arquivo de imagem, codificado em qualquer formato em que esteja o arquivo de imagem ou em valores de pixel RGB?

Outras respostas aqui mostram como ler um arquivo em um arquivo byte[]. Você byte[]conterá o conteúdo exato do arquivo e você precisará decodificá-lo para fazer qualquer coisa com os dados da imagem.

A API padrão do Java para leitura (e gravação) de imagens é a API ImageIO, que você pode encontrar no pacote javax.imageio. Você pode ler uma imagem de um arquivo com apenas uma única linha de código:

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

Isso lhe dará um BufferedImage, não um byte[]. Para acessar getRaster()os dados da imagem, você pode acessar o BufferedImage. Isso fornecerá um Rasterobjeto, que possui métodos para acessar os dados de pixel (possui vários getPixel()/ getPixels()métodos).

Pesquisar a documentação da API para javax.imageio.ImageIO, java.awt.image.BufferedImage, java.awt.image.Rasteretc.

O ImageIO suporta vários formatos de imagem por padrão: JPEG, PNG, BMP, WBMP e GIF. É possível adicionar suporte para mais formatos (você precisaria de um plug-in que implemente a interface do provedor de serviços ImageIO).

Consulte também o seguinte tutorial: Trabalhando com imagens


16

No caso de alguém ainda estar procurando uma solução sem dependência e se você tiver um arquivo .

1) DataInputStream

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

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

3) RandomAccessFile

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

Digamos, e se a matriz de bytes for muito grande, o que poderia causar OOM para o heap? Existe uma solução semelhante que usará o JNI para armazenar os bytes e, mais tarde, poderemos usar o inputStream a partir dos dados armazenados lá (tipo de cache temporário)?
desenvolvedor android

14

Se você não deseja usar a biblioteca do Apache commons-io, esse trecho é retirado da classe sun.misc.IOUtils. É quase duas vezes mais rápido que a implementação comum usando 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;
}

Essa é uma solução um pouco estranha, o comprimento é um limite superior do comprimento da matriz. Se você conhece o tamanho, tudo o que precisa é: byte [] output = new byte [length]; is.read (saída); (mas veja minha resposta)
Luke Hutchison

@ luke-hutchison como eu disse, esta é a solução do sun.misc.IOUtils. Nos casos mais comuns, você não sabe o tamanho de um InputStream antecipadamente, portanto, se (length == -1) length = Integer.MAX_VALUE; aplica-se. Essa solução funciona, mesmo se o comprimento especificado for maior que o comprimento do InputStream.
Kristian Kraljic 31/07/2015

@LukeHutchison Se você souber o comprimento, poderá lidar com ele com algumas linhas. Se você olhar para cada resposta, todo mundo está reclamando que o comprimento não é conhecido. Finalmente, uma resposta que é padrão, pode ser usada com o Java 7 Android e não requer nenhuma biblioteca externa.
Csaba Toth 27/02

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

8

@ Adamski: Você pode evitar o buffer inteiramente.

Código copiado de http://www.exampledepot.com/egs/java.io/File2ByteArray.html (Sim, é muito detalhado, mas precisa da metade do tamanho da memória que a outra solução.)

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

5
Depende do tamanho do conhecimento antecipadamente.
amigos estão dizendo sobre stolsvik

2
Claro, mas eles devem saber o tamanho: "Eu quero ler uma imagem"
pihentagy

1
se você souber o tamanho, o java fornecerá o código para você. veja minha resposta ou google para "DataInputStream" e é método readFully.
Dermoritz 28/05

Você deve adicionar is.close()se offset < bytes.lengthou InputStreamnão será fechado se essa exceção for lançada.
Jared Rummler

3
Então é melhor, você deve usar try-with-recursos
pihentagy

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

No entanto, geralmente o sistema operacional já possui buffers suficientes para que isso não seja uma grande preocupação para arquivos menores. Não é como se a cabeça do disco rígido lesse cada byte separadamente (um disco rígido é uma placa de vidro giratória com informações magnéticas, um pouco como o ícone estranho que usamos para salvar dados: P).
Maarten Bodewes

6
@Maarten Bodewes: a maioria dos dispositivos possui um tipo de transferência de bloco; portanto, nem toda leitura () causa um acesso real ao dispositivo, mas ter uma chamada do SO por byte já é suficiente para diminuir o desempenho. Enquanto terminava o InputStreamem um BufferedInputStreamantes que o código reduziria os-chamadas e mitigar os inconvenientes de desempenho significativamente, que o código ainda vai fazer o trabalho de cópia manual desnecessária de um tampão para outro.
Holger

4

O Java 9 finalmente fornecerá um bom método:

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

4
Qual é a diferença entre isso e InputStram.readAllBytes()aquilo é one-liner?
Slava Semushin

2

Sei que é tarde demais, mas aqui acho que é uma solução mais limpa, mais legível ...

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

4
Você deve usar try-with-resources.
Victor Stafusa

Sua organização no final precisa ser feita em um bloco finalmente em caso de erros, caso contrário, isso pode causar um vazamento de memória.
MGDavies

2

Modo Java 8 (graças a BufferedReader e 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>);
    }
}

Observe que esta solução limpa o retorno de carro ('\ r') e pode ser inapropriada.


4
Isso é para String. OP está pedindo byte[].
FrozenFire

Não é apenas \risso que poderia ser um problema. Este método converte os bytes em caracteres e vice-versa (usando o conjunto de caracteres padrão para InputStreamReader). Quaisquer bytes que não sejam válidos na codificação de caracteres padrão (por exemplo, -1 para UTF-8 no Linux) serão corrompidos, possivelmente alterando o número de bytes.
seanf

Parece que esta é uma boa resposta, mas orientada para o texto. Comprador, cuidado.
Wheezil

1

Tentei editar a resposta de @ numan com uma correção para gravar dados de lixo, mas a edição foi rejeitada. Embora esse pequeno pedaço de código não seja nada brilhante, não vejo outra resposta melhor. Aqui está o que faz mais sentido para mim:

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 não precisa ser fechado. construções try / finalmente omitidas para facilitar a leitura


1

Veja a InputStream.available()documentação:

É particularmente importante perceber que você não deve usar esse método para dimensionar um contêiner e assumir que é possível ler a totalidade do fluxo sem precisar redimensionar o contêiner. Esses chamadores provavelmente devem escrever tudo o que lêem em um ByteArrayOutputStream e convertê-lo em uma matriz de bytes. Como alternativa, se você estiver lendo um arquivo, File.length retornará o tamanho atual do arquivo (embora suponha que o tamanho do arquivo não possa ser alterado pode estar incorreto, a leitura de um arquivo é inerentemente atrevida).


1

Embrulhe-o em um DataInputStream, se isso estiver fora da mesa, por algum motivo, use read para martelá-lo até obter um -1 ou todo o bloco que você pediu.

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

1

Estamos vendo algum atraso para poucas transações da AWS, ao converter o objeto S3 em ByteArray.

Nota: O objeto S3 é um documento PDF (o tamanho máximo é de 3 mb).

Estamos usando a opção 1 (org.apache.commons.io.IOUtils) para converter o objeto S3 em ByteArray. Observamos que o S3 fornece o método IOUtils do inbuild para converter o objeto S3 em ByteArray, solicitamos que você confirme qual é a melhor maneira de converter o objeto S3 em ByteArray para evitar o atraso.

Opção 1:

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

Opção 2:

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

Além disso, deixe-me saber se temos outra maneira melhor de converter o objeto s3 para bytearray


0

O outro caso para obter a matriz de bytes correta via fluxo, após enviar a solicitação ao servidor e aguardar a resposta.

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

0

Você está fazendo uma cópia extra se usar ByteArrayOutputStream. Se você conhece o tamanho do fluxo antes de começar a lê-lo (por exemplo, o InputStream é na verdade um FileInputStream e pode chamar file.length () no arquivo, ou o InputStream é uma entrada do arquivo zip InputStream e pode chamar zipEntry. length ()), então é muito melhor escrever diretamente na matriz de bytes [] - ele usa metade da memória e economiza tempo.

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

Nota: a última linha acima lida com arquivos sendo truncados enquanto o fluxo está sendo lido, se você precisar lidar com essa possibilidade, mas se o arquivo ficar mais longo enquanto o fluxo estiver sendo lido, o conteúdo da matriz byte [] não será aumentado para incluir o novo conteúdo do arquivo, a matriz será simplesmente truncada para o comprimento antigo inputStreamLength .


0

Eu uso isso.

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

2
Adicione um pouco de explicação com a resposta de como essa resposta ajuda OP na fixação edição atual
ρяσѕρєя K

0

Esta é minha versão copiar e colar:

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

2
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
precisa saber é o seguinte

0

Java 7 e posterior:

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

20
sun.misc.IOUtilsnão é "Java 7". É uma classe específica de implementação proprietária que pode não estar presente em outras implementações do JRE e pode desaparecer sem nenhum aviso em um dos próximos releases.
Holger


0

Aqui está uma versão otimizada, que tenta evitar o máximo possível de copiar bytes de dados:

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

0

Solução no Kotlin (também funcionará em Java, é claro), que inclui os dois casos em que você sabe o tamanho ou não:

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

Se você souber o tamanho, poupará o dobro da memória usada em comparação com as outras soluções (em um breve momento, mas ainda pode ser útil). Isso porque você precisa ler o fluxo inteiro até o final e depois convertê-lo em uma matriz de bytes (semelhante ao ArrayList que você converte em apenas uma matriz).

Portanto, se você estiver no Android, por exemplo, e tiver um Uri para lidar, tente obter o tamanho usando este:

    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
    }

-1
/*InputStream class_InputStream = null;
I am reading class from DB 
class_InputStream = rs.getBinaryStream(1);
Your Input stream could be from any source
*/
int thisLine;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((thisLine = class_InputStream.read()) != -1) {
    bos.write(thisLine);
}
bos.flush();
byte [] yourBytes = bos.toByteArray();

/*Don't forget in the finally block to close ByteArrayOutputStream & InputStream
 In my case the IS is from resultset so just closing the rs will do it*/

if (bos != null){
    bos.close();
}

Fechar e liberar bos é um desperdício de cliques no teclado. Fechar o fluxo de entrada tem mais chances de ajudar. Ler um byte de cada vez é ineficiente. Veja a resposta de numan.
akostadinov
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.