Converti InputStream in array di byte in Java


Risposte:


1136

È possibile utilizzare Apache Commons IO per gestire questo e altri compiti simili.

Il IOUtilstipo ha un metodo statico per leggere an InputStreame restituire a byte[].

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

Internamente questo crea un ByteArrayOutputStreame copia i byte nell'output, quindi chiama toByteArray(). Gestisce file di grandi dimensioni copiando i byte in blocchi di 4KiB.


188
Per la necessità di scrivere 4 righe di codice, pensi che valga la pena importare una dipendenza di terze parti?
oxbow_lakes,

217
Se esiste una libreria che gestisce il requisito e si occupa dell'elaborazione per file di grandi dimensioni ed è ben testata, sicuramente la domanda è: perché dovrei scriverla da solo? Il barattolo è solo 107 KB e se hai bisogno di un metodo da esso, è probabile che tu ne usi anche altri
Rich Seller

242
@oxbow_lakes: considerando la quantità sbalorditiva di implementazioni errate di questa funzionalità che ho visto nella mia vita di sviluppatore, sento che , vale davvero la dipendenza esterna per farlo bene.
Joachim Sauer,

17
Perché non andare a dare un'occhiata alle cose comuni di Apache FastArrayListo alle loro mappe di riferimento deboli e deboli e tornare a dirmi quanto è "ben collaudata" questa libreria. È un mucchio di immondizia
oxbow_lakes,

87
Oltre a Apache commons-io, controlla la classe ByteStreams di Google Guava . InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
michaelok,

446

Devi leggere ogni byte dal tuo InputStreame scriverlo in a ByteArrayOutputStream.

È quindi possibile recuperare l'array di byte sottostante chiamando 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
Che dire della dimensione del byte appena creato []. Perché è il 16384? Come posso determinare la giusta dimensione esatta? Grazie mille.
Ondrej Bozek,

6
16384 è una scelta abbastanza arbitraria anche se tendo a favorire i poteri di 2 per aumentare la possibilità che l'array si allinei con i confini delle parole. La risposta di pihentagy mostra come evitare di utilizzare un buffer intermedio, ma piuttosto allocare un array della dimensione corretta. A meno che tu non abbia a che fare con file di grandi dimensioni, preferisco personalmente il codice sopra, che è più elegante e può essere utilizzato per InputStreams in cui il numero di byte da leggere non è noto in anticipo.
Adamski,

@Adamski La creazione di array di byte non è molto più grande di quanto pensi che i dati sarebbero nello stream, sprecando la memoria?
Paul Brewczynski,

@bluesm: Sì, è corretto. Tuttavia, nel mio esempio l'array di byte è solo 16 KB e così piccolo per gli standard di oggi. Inoltre, ovviamente questa memoria verrà nuovamente liberata in seguito.
Adamski,

5
@Adamski Molti componenti hardware, web server e componenti di livello OS dell'infrastruttura utilizzano i buffer 4K per spostare i dati, quindi questo è il motivo del numero esatto, ma il punto principale è che si ottiene un così piccolo aumento delle prestazioni andando su 4K che è generalmente considerato uno spreco di memoria. Suppongo che sia ancora vero, perché avevo una conoscenza decennale!


132

Usa vanilla Java DataInputStreame il suo readFullymetodo (esiste almeno da Java 1.4):

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

Esistono altri aspetti di questo metodo, ma lo uso sempre per questo caso d'uso.


45
+1 per l'utilizzo delle librerie standard anziché di una dipendenza di terze parti. Sfortunatamente non funziona per me perché non conosco la lunghezza del flusso in anticipo.
Andrew Spencer,

2
che cos'è imgFile? Non può essere un InputStream, che doveva essere l'input di questo metodo
Janus Troelsen,

4
@janus è un "File". in questo modo funziona solo se conosci la lunghezza del file o il conteggio dei byte da leggere.
Dermoritz,

5
Cosa interessante, ma devi conoscere la lunghezza esatta dello stream (parte del) da leggere. Inoltre, la classe DataInputStreamviene utilizzata principalmente per leggere i tipi primari (Long, Shorts, Char ...) da un flusso, quindi possiamo vedere questo utilizzo come un uso improprio della classe.
Olivier Faucheux

17
Se conosci già la lunghezza dei dati da leggere dallo stream, questo non è meglio di InputStream.read.
Logan Pickup,

119

Se ti capita di usare google guava , sarà semplice come:

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

8
ByteStreamsè annotato con@Beta
Kid101 il

46

Come sempre, anche il framework Spring (spring-core dal 3.2.2) ha qualcosa per te:StreamUtils.copyToByteArray()


Come la maggior parte degli altri, volevo evitare di usare una libreria di terze parti per qualcosa di così semplice, ma Java 9 non è un'opzione al momento ... per fortuna, stavo già usando 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
È un esempio e come tale, la brevità è all'ordine del giorno. Anche la restituzione di null qui sarebbe la scelta corretta in alcuni casi (anche se in un ambiente di produzione si avrebbe anche una corretta gestione delle eccezioni e documentazione).

11
Capisco la brevità in un esempio, ma perché non fare semplicemente in modo che il metodo di esempio lanci IOException piuttosto che ingoiarlo e restituire un valore insignificante?
pendente

4
mi sono preso la libertà di cambiare da 'return null' a 'throw IOException'
kritzikratzi

3
Qui non è necessario provare con le risorse, perché ByteArrayOutputStream # close () non fa nulla. (ByteArrayOutputStream # flush () non è necessario e non fa nulla.)
Luke Hutchison,

25

Soluzione sicura (con capacità diclosestreaming correttamente):

  • Versione Java 9+:

    final byte[] bytes;
    try (inputStream) {
        bytes = inputStream.readAllBytes();
    }
  • Versione 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);
            }
        }
    }
  • Versione di Kotlin (quando Java 9+ non è accessibile):

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

    Per evitare nidificati usevedi qui .


Non significa che ad un certo punto avresti il ​​doppio della memoria utilizzata, perché hai sia il buffer che l'array di byte? Non esiste un modo per inviare i byte direttamente all'array di byte di output?
sviluppatore Android

@androiddeveloper; Mi dispiace. Non conosco la risposta! Ma io non la penso così. Penso che in questo modo (usando il buffer) sia un modo ottimizzato.
Mir-Ismaili,

Ho controllato e lo fa, ma sembra che sia l'unica soluzione che puoi scegliere quando non conosci le dimensioni. Se conosci già la dimensione, puoi creare direttamente l'array di byte con la dimensione specificata e riempirla. Quindi, si utilizza una funzione che otterrà un parametro della dimensione in byte e, se è valida, lo si utilizza per creare e riempire direttamente l'array di byte, senza creare nessun altro oggetto di grandi dimensioni.
sviluppatore Android

@androiddeveloper; Grazie per le informazioni. Non li conoscevo.
Mir-Ismaili,

19

Hai davvero bisogno dell'immagine come byte[]? Cosa ti aspetti esattamente nelbyte[] : il contenuto completo di un file di immagine, codificato in qualsiasi formato in cui si trova il file di immagine, o valori di pixel RGB?

Altre risposte qui mostrano come leggere un file in a byte[]. Il tuo byte[]conterrà il contenuto esatto del file, e avresti bisogno di decodifica che per fare qualsiasi cosa con i dati dell'immagine.

L'API standard di Java per la lettura (e la scrittura) di immagini è l'API ImageIO, che puoi trovare nel pacchetto javax.imageio. Puoi leggere un'immagine da un file con una sola riga di codice:

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

Questo ti darà un BufferedImage, non un byte[]. Per ottenere i dati dell'immagine, è possibile chiamare getRaster()il BufferedImage. Questo ti darà un Rasteroggetto, che ha metodi per accedere ai dati pixel (ha diversi getPixel()/ getPixels()metodi).

Occhiata la documentazione API per javax.imageio.ImageIO, java.awt.image.BufferedImage, java.awt.image.Rasteretc.

ImageIO supporta una serie di formati di immagine per impostazione predefinita: JPEG, PNG, BMP, WBMP e GIF. È possibile aggiungere supporto per più formati (sarebbe necessario un plug-in che implementa l'interfaccia del provider di servizi ImageIO).

Vedi anche il seguente tutorial: Lavorare con le immagini


16

Nel caso qualcuno stia ancora cercando una soluzione senza dipendenza e se hai un file .

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

Supponiamo che l'array di byte sia troppo grande, il che potrebbe causare OOM per l'heap? Esiste una soluzione simile che utilizzerà JNI per archiviare i byte e in seguito saremo in grado di utilizzare inputStream dai dati memorizzati lì (sorta di cache temporanea)?
sviluppatore Android

14

Se non vuoi usare la libreria commons-io di Apache, questo frammento è preso dalla classe sun.misc.IOUtils. È quasi due volte più veloce dell'implementazione comune utilizzando 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;
}

Questa è una soluzione un po 'strana, la lunghezza è un limite superiore alla lunghezza dell'array. Se conosci la lunghezza, tutto ciò che serve è: byte [] output = new byte [lunghezza]; is.read (uscita); (ma vedi la mia risposta)
Luke Hutchison il

@ luke-hutchison come ho detto, questa è la soluzione di sun.misc.IOUtils. Nei casi più comuni non si conosce la dimensione di un InputStream in anticipo, quindi if (length == -1) length = Integer.MAX_VALUE; si applica. Questa soluzione funziona, anche se la lunghezza data è maggiore della lunghezza di InputStream.
Kristian Kraljic,

@LukeHutchison Se conosci la lunghezza puoi gestirla con poche righe. Se guardi ogni risposta, tutti si lamentano che la lunghezza non è nota. Finalmente una risposta che è standard, può essere utilizzata con Java 7 Android e non richiede alcuna libreria esterna.
Csaba Toth,

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: puoi evitare del tutto il buffer.

Codice copiato da http://www.exampledepot.com/egs/java.io/File2ByteArray.html (Sì, è molto dettagliato, ma richiede l'altra metà della memoria come l'altra soluzione.)

// 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
Dipende dalla conoscenza anticipata delle dimensioni.
stolsvik,

2
Certo, ma dovrebbero conoscere le dimensioni: "Voglio leggere un'immagine"
pihentagy,

1
se conosci le dimensioni, java ti fornisce il codice. vedi la mia risposta o google per "DataInputStream" ed è il metodo readFully.
Dermoritz,

È necessario aggiungere is.close()se offset < bytes.lengtho InputStreamnon verrà chiuso se viene generata quell'eccezione.
Jared Rummler,

3
Quindi, dovresti usare try-with-resources
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();

Tuttavia, di solito il sistema operativo ha già un buffer sufficiente per non essere una grande preoccupazione per i file più piccoli. Non è come se la testa del disco rigido leggesse ogni byte separatamente (un disco rigido è una lastra di vetro che gira con informazioni magnetiche codificate su di esso, un po 'come quella strana icona che usiamo per salvare i dati: P).
Maarten Bodewes,

6
@Maarten Bodewes: la maggior parte dei dispositivi ha una sorta di trasferimento a blocchi, quindi non tutti read () causeranno effettivamente un accesso effettivo al dispositivo, ma avere una chiamata OS per byte è già sufficiente per uccidere le prestazioni. Se si racchiude il codice InputStreamin a BufferedInputStreamprima che il codice ridurrebbe le chiamate del sistema operativo e mitigherebbe in modo significativo gli svantaggi delle prestazioni, quel codice eseguirà comunque operazioni di copia manuale non necessarie da un buffer a un altro.
Holger,

4

Java 9 ti darà finalmente un bel metodo:

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

4
Qual è la differenza tra questo e InputStram.readAllBytes()quello è one-liner?
Slava Semushin,

2

So che è troppo tardi ma qui penso che sia una soluzione più pulita più leggibile ...

/**
 * 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
Dovresti usare try-with-resources.
Victor Stafusa,

Il riordino alla fine deve essere eseguito in un blocco finalmente in caso di errori, altrimenti ciò potrebbe causare una perdita di memoria.
MGDavies il

2

Java 8 way (grazie 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>);
    }
}

Nota che questa soluzione asciuga ritorno a capo ( '\ r') e può essere inappropriato.


4
Questo è per String. OP sta chiedendo byte[].
FrozenFire,

Non è solo \rche potrebbe essere un problema. Questo metodo converte i byte in caratteri e viceversa (utilizzando il set di caratteri predefinito per InputStreamReader). Tutti i byte non validi nella codifica dei caratteri predefinita (ad esempio -1 per UTF-8 su Linux) verranno danneggiati, cambiando potenzialmente anche il numero di byte.
seanf,

Sembra che questa sia una buona risposta, ma orientata al testo. Compratore stai attento.
Wheezil,

1

Ho provato a modificare la risposta di @ numan con una correzione per la scrittura di dati inutili ma la modifica è stata respinta. Mentre questo breve pezzo di codice non è niente di eccezionale, non vedo altre risposte migliori. Ecco cosa ha più senso per 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 non deve essere chiuso. prova / finalmente costruisce omesso per leggibilità


1

Vedi la InputStream.available()documentazione:

È particolarmente importante rendersi conto che non è necessario utilizzare questo metodo per ridimensionare un contenitore e supporre che sia possibile leggere l'intero flusso senza dover ridimensionare il contenitore. Tali chiamanti dovrebbero probabilmente scrivere tutto ciò che leggono in un ByteArrayOutputStream e convertirlo in un array di byte. In alternativa, se stai leggendo da un file, File.length restituisce la lunghezza corrente del file (anche se supponendo che la lunghezza del file non possa essere modificata potrebbe essere errata, la lettura di un file è intrinsecamente rigida).


1

Avvolgilo in un DataInputStream se questo è fuori dal tavolo per qualche motivo, basta usare read per martellare su di esso fino a quando non ti dà un -1 o l'intero blocco richiesto.

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

Stiamo riscontrando un certo ritardo per alcune transazioni AWS, durante la conversione dell'oggetto S3 in ByteArray.

Nota: l'oggetto S3 è un documento PDF (la dimensione massima è 3 mb).

Stiamo usando l'opzione # 1 (org.apache.commons.io.IOUtils) per convertire l'oggetto S3 in ByteArray. Abbiamo notato che S3 fornisce il metodo IOUtils inbuild per convertire l'oggetto S3 in ByteArray, ti chiediamo di confermare qual è il modo migliore per convertire l'oggetto S3 in ByteArray per evitare il ritardo.

Opzione 1:

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

Opzione 2:

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

Fammi sapere se esiste un altro modo migliore per convertire l'oggetto s3 in bytearray


0

L'altro caso per ottenere l'array di byte corretto tramite stream, dopo aver inviato la richiesta al server e in attesa della risposta.

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

Stai facendo una copia extra se usi ByteArrayOutputStream. Se conosci la lunghezza dello stream prima di iniziare a leggerlo (ad esempio, InputStream è in realtà un FileInputStream e puoi chiamare file.length () sul file o InputStream è una voce del file zip InputStream e puoi chiamare zipEntry. lunghezza ()), quindi è molto meglio scrivere direttamente nell'array byte []: utilizza metà della memoria e consente di risparmiare 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);

NB l'ultima riga sopra tratta dei file che vengono troncati durante la lettura dello stream, se è necessario gestire tale possibilità, ma se il file si allunga durante la lettura dello stream, il contenuto dell'array byte [] non verrà allungato per includere il nuovo contenuto del file, l'array verrà semplicemente troncato alla vecchia lunghezza inputStreamLength .


0

Io lo uso

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
Aggiungi una spiegazione con la risposta su come questa risposta aiuta OP nel risolvere il problema attuale
ρяσѕρєя K

0

Questa è la mia versione copia-incolla:

@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
Sebbene questo frammento di codice possa risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che in futuro stai rispondendo alla domanda dei lettori e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
Ferrybig

0

Java 7 e versioni successive:

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

20
sun.misc.IOUtilsnon è "Java 7". È una classe proprietaria specifica per l'implementazione che potrebbe non essere presente in altre implementazioni JRE e che può scomparire senza alcun preavviso in una delle prossime versioni.
Holger,


0

Ecco una versione ottimizzata, che cerca di evitare il più possibile la copia dei byte di dati:

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

Soluzione in Kotlin (funzionerà anche in Java, ovviamente), che include entrambi i casi in cui si conosce la dimensione o meno:

    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 conosci le dimensioni, ti consente di risparmiare il doppio della memoria utilizzata rispetto alle altre soluzioni (in un breve momento, ma potrebbe comunque essere utile). Questo perché devi leggere l'intero flusso fino alla fine, quindi convertirlo in un array di byte (simile a ArrayList che converti in un solo array).

Quindi, se sei su Android, ad esempio, e hai qualche Uri da gestire, puoi provare a ottenere le dimensioni usando questo:

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

La chiusura e lo svuotamento del bos è uno spreco di clic sulla tastiera. La chiusura del flusso di input ha maggiori probabilità di essere d'aiuto. La lettura di un byte alla volta è inefficiente. Vedi la risposta di numan.
Akostadinov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.