Konwertuj InputStream na tablicę bajtów w Javie


Odpowiedzi:


1135

Możesz używać Apache Commons IO do obsługi tego i podobnych zadań.

IOUtilsTyp ma statyczną metodę przeczytać InputStreami zwrócić byte[].

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

Wewnętrznie tworzy to ByteArrayOutputStreami kopiuje bajty na wyjście, a następnie wywołuje toByteArray(). Obsługuje duże pliki, kopiując bajty w blokach 4KiB.


188
Jeśli chcesz napisać 4 wiersze kodu, uważasz, że warto zaimportować zależność innej firmy?
oxbow_lakes

217
Jeśli istnieje biblioteka, która obsługuje ten wymóg i zajmuje się przetwarzaniem dużych plików i jest dobrze przetestowana, z pewnością pytanie brzmi: dlaczego sam bym to napisał? Słoik ma tylko 107 KB, a jeśli potrzebujesz jednej metody, prawdopodobnie również użyjesz innych
Rich Seller

242
@oxbow_lakes: Biorąc pod uwagę oszałamiającą liczbę niewłaściwych implementacji tej funkcji, które widziałem w życiu programisty, czuję, że tak , bardzo warto zewnętrzną zależność, aby zrobić to dobrze.
Joachim Sauer

17
Dlaczego nie pójść i rzucić okiem na takie FastArrayListwspólne Apache lub ich miękkie i słabe mapy referencyjne, i wróć, aby powiedzieć mi, jak „dobrze przetestowana” ta biblioteka. To kupa śmieci
oxbow_lakes

87
Oprócz Apache commons-io, sprawdź klasę ByteStreams od Google Guava . InputStream is; byte[] filedata=ByteStreams.toByteArray(is);
michaelok

446

Musisz odczytać każdy bajt ze swojego InputStreami zapisać go w ByteArrayOutputStream.

Następnie można pobrać podstawową tablicę bajtów, wywołując 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();

16
Co z rozmiarem nowo utworzonego bajtu []. Dlaczego to 16384? Jak mogę określić dokładnie odpowiedni rozmiar? Dziękuję Ci bardzo.
Ondrej Bozek

6
16384 jest dość arbitralnym wyborem, chociaż mam tendencję do faworyzowania potęg 2, aby zwiększyć szansę wyrównania tablicy z granicami słów. Odpowiedź pihentagy pokazuje, jak można uniknąć używania bufora pośredniego, ale raczej przydzielić tablicę o odpowiednim rozmiarze. O ile nie masz do czynienia z dużymi plikami, osobiście wolę powyższy kod, który jest bardziej elegancki i może być używany do InputStreams, w których liczba bajtów do odczytania nie jest wcześniej znana.
Adamski,

@Adamski Czy tworzenie tablicy bajtów nie jest o wiele większe niż można oczekiwać w danych w strumieniu, marnować pamięć?
Paul Brewczynski

@bluesm: Tak, to prawda. Jednak w moim przykładzie tablica bajtów ma tylko 16 KB i jest tak mała jak na dzisiejsze standardy. Oczywiście pamięć ta zostanie później ponownie zwolniona.
Adamski

5
@Adamski Wiele urządzeń infrastruktury, serwerów sieciowych i komponentów warstwy systemu operacyjnego używa buforów 4K do przenoszenia danych, więc to jest powód dokładnej liczby, ale najważniejsze jest to, że uzyskuje się tak niewielki wzrost wydajności, przekraczając 4K że jest ogólnie uważane za marnotrawstwo pamięci. Zakładam, że to nadal prawda, ponieważ miałem dziesięcioletnią wiedzę, którą miałem!


132

Użyj waniliowej Java DataInputStreami jej readFullymetody (istnieje od co najmniej Java 1.4):

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

Istnieje kilka innych smaków tej metody, ale używam tego cały czas w tym przypadku użycia.


45
+1 za korzystanie ze standardowych bibliotek zamiast zależności innej firmy. Niestety nie działa dla mnie, ponieważ nie znam początkowej długości strumienia.
Andrew Spencer

2
co to jest imgFile? To nie może być InputStream, który miał być wkładem tej metody
Janus Troelsen

4
@janus jest to „plik”. w ten sposób działa tylko wtedy, gdy znasz długość pliku lub liczbę bajtów do odczytania.
dermoritz

5
Interesująca rzecz, ale musisz znać dokładną długość (części) strumienia do odczytania. Co więcej, klasa DataInputStreamjest używana głównie do odczytywania typów podstawowych (długich, krótkich, znaków ...) ze strumienia, dzięki czemu możemy postrzegać to użycie jako niewłaściwe użycie klasy.
Olivier Faucheux

17
Jeśli znasz już długość danych do odczytu ze strumienia, nie jest to lepsze niż InputStream.read.
Logan Pickup

119

Jeśli zdarzy ci się używać google guava , będzie to tak proste, jak:

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

8
ByteStreamsjest opatrzony adnotacją@Beta
Kid101,

46

Jak zawsze również Spring Framework (Spring-Core od 3.2.2) ma coś dla Ciebie:StreamUtils.copyToByteArray()


Jak większość innych, chciałem uniknąć korzystania z biblioteki innej firmy do czegoś tak prostego, ale Java 9 nie jest w tej chwili opcją ... na szczęście już korzystałem z Springa.
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
Jest to przykład i zwięzłość jest na porządku dziennym. Zwrócenie tutaj wartości null byłoby właściwym wyborem w niektórych przypadkach (chociaż w środowisku produkcyjnym miałbyś również odpowiednią obsługę wyjątków i dokumentację).

11
Rozumiem zwięzłość w przykładzie, ale dlaczego nie po prostu sprawić, aby przykładowa metoda generowała wyjątek IOException zamiast połykać go i zwracać bezwartościową wartość?
pendor

4
skorzystałem z możliwości zmiany z „return null” na „throw IOException”
kritzikratzi

3
Try-with-resources nie jest tutaj potrzebne, ponieważ ByteArrayOutputStream # close () nic nie robi. (ByteArrayOutputStream # flush () nie jest potrzebny i też nie robi nic.)
Luke Hutchison

25

Bezpieczne rozwiązanie (z możliwościącloseprawidłowego przesyłania strumieni):

  • Wersja Java 9+:

    final byte[] bytes;
    try (inputStream) {
        bytes = inputStream.readAllBytes();
    }
  • Wersja 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);
            }
        }
    }
  • Wersja Kotlin (gdy Java 9+ nie jest dostępna):

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

    Aby uniknąć zagnieżdżenia, usezobacz tutaj .


Czy to nie znaczy, że w pewnym momencie użyłbyś podwójnej pamięci, ponieważ masz zarówno bufor, jak i tablicę bajtów? Czy nie ma sposobu, aby wysłać bajty bezpośrednio do wyjściowej tablicy bajtów?
programista Androida

@androiddeveloper; Przepraszam. Nie znam odpowiedzi! Ale nie sądzę. Myślę, że ten sposób (użycie bufora) jest sposobem zoptymalizowanym.
Mir-Ismaili

Sprawdziłem i tak, ale wydaje się, że jest to jedyne rozwiązanie, które możesz wybrać, gdy nie znasz rozmiaru. Jeśli znasz już rozmiar, możesz bezpośrednio utworzyć tablicę bajtów o podanym rozmiarze i wypełnić go. Tak więc używasz funkcji, która otrzyma parametr wielkości bajtu, a jeśli jest poprawna, użyj go do bezpośredniego utworzenia i wypełnienia tablicy bajtów, bez tworzenia żadnego innego dużego obiektu.
programista Androida

@androiddeveloper; Dziękuję za informację. Nie znałem ich.
Mir-Ismaili

19

Czy naprawdę potrzebujesz tego obrazu jako byte[]? Czego dokładnie oczekujesz wbyte[] - pełnej zawartości pliku obrazu, zakodowanego w jakimkolwiek formacie, w którym znajduje się plik obrazu, lub wartości pikseli RGB?

Inne odpowiedzi tutaj pokazują, jak wczytać plik do pliku byte[]. Twój byte[]będzie zawierać dokładną zawartość pliku, i trzeba by dekodowania że nic zrobić z danymi obrazu.

Standardowym API Java do odczytu (i zapisu) obrazów jest ImageIO API, które można znaleźć w pakiecie javax.imageio. Możesz odczytać obraz z pliku za pomocą tylko jednego wiersza kodu:

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

To da ci, a BufferedImagenie byte[]. Aby uzyskać dane obrazu, możesz zadzwonić getRaster()na BufferedImage. To da ci Rasterobiekt, który ma metody dostępu do danych pikseli (ma kilka metod getPixel()/ getPixels()).

Odnośnika do dokumentacji API javax.imageio.ImageIO, java.awt.image.BufferedImage, java.awt.image.Rasteritd.

ImageIO domyślnie obsługuje wiele formatów obrazu: JPEG, PNG, BMP, WBMP i GIF. Możliwe jest dodanie obsługi większej liczby formatów (potrzebujesz wtyczki, która implementuje interfejs dostawcy usług ImageIO).

Zobacz także następujący samouczek: Praca z obrazami


16

W przypadku, gdy ktoś nadal szuka rozwiązania bez zależności i jeśli masz plik .

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

Powiedzmy, co jeśli tablica bajtów jest zbyt duża, co może spowodować OOM dla sterty? Czy istnieje podobne rozwiązanie, które będzie wykorzystywać JNI do przechowywania bajtów, a później będziemy mogli użyć inputStream z przechowywanych tam danych (rodzaj tymczasowej pamięci podręcznej)?
programista Androida

14

Jeśli nie chcesz korzystać z biblioteki Apache commons-io, ten fragment kodu jest pobierany z klasy sun.misc.IOUtils. Jest prawie dwa razy szybszy niż zwykła implementacja przy użyciu 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;
}

Jest to trochę dziwne rozwiązanie, długość jest górną granicą długości tablicy. Jeśli znasz długość, wystarczy: byte [] output = new byte [length]; is.read (wyjście); (ale zobacz moją odpowiedź)
Luke Hutchison

@ luke-hutchison, jak powiedziałem, to rozwiązanie sun.misc.IOUtils. W najczęstszych przypadkach nie znasz z góry rozmiaru InputStream, więc jeśli (length == -1) length = Integer.MAX_VALUE; dotyczy. To rozwiązanie działa, nawet jeśli podana długość jest większa niż długość InputStream.
Kristian Kraljic

@LukeHutchison Jeśli znasz długość, możesz poradzić sobie z kilkoma liniami. Jeśli spojrzysz na każdą odpowiedź, wszyscy narzekają, że długość nie jest znana. Na koniec odpowiedź, która jest standardowa, może być używana z Java 7 Android i nie wymaga żadnej zewnętrznej biblioteki.
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: Możesz całkowicie uniknąć buforowania.

Kod skopiowany z http://www.exampledepot.com/egs/java.io/File2ByteArray.html (Tak, jest bardzo szczegółowy, ale potrzebuje innego rozmiaru pamięci niż inne rozwiązanie).

// 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
Zależy od znajomości rozmiaru z góry.
stolsvik,

2
Oczywiście, ale powinni znać rozmiar: „Chcę przeczytać obraz”
pihentagy,

1
jeśli znasz rozmiar, java dostarcza kod dla ciebie. zobacz moją odpowiedź lub google dla „DataInputStream” i jest to metoda readFully.
dermoritz

Powinieneś dodać, is.close()czy offset < bytes.lengthlub InputStreamnie zostanie zamknięty, jeśli ten wyjątek zostanie zgłoszony.
Jared Rummler

3
Zatem lepiej, powinieneś użyć 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();

Jednak zwykle system operacyjny już buforuje wystarczająco dużo, aby nie było to wielkim zmartwieniem dla mniejszych plików. To nie tak, że głowica dysku twardego odczytuje każdy bajt osobno (dysk twardy to obracająca się szklana płytka z kodowanymi magnetycznie informacjami, trochę jak ta dziwna ikona, której używamy do zapisywania danych: P).
Maarten Bodewes

6
@Maarten Bodewes: większość urządzeń ma pewien rodzaj transferu bloków, więc nie każdy odczyt () spowoduje rzeczywiście dostęp do urządzenia, ale posiadanie wywołania OS na bajt jest już wystarczające, aby zabić wydajność. Podczas owijania InputStreamw sposób BufferedInputStreamprzed tym kodem zmniejszyłoby OS połączeń i złagodzenie niedogodności wydajność znacznie, że kod będzie nadal robić niepotrzebnej pracy ręcznej kopiowania z jednego bufora do innego.
Holger

4

Java 9 da ci wreszcie fajną metodę:

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

4
Jaka jest różnica między tym a tym, InputStram.readAllBytes()że jest to jedna linijka?
Slava Semushin

2

Wiem, że jest za późno, ale myślę, że jest to czystsze rozwiązanie, które jest bardziej czytelne ...

/**
 * 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
Powinieneś użyć try-with-resources.
Victor Stafusa

Sprzątanie na końcu musi być wykonane w ostatecznym bloku na wypadek błędów, w przeciwnym razie może to spowodować wyciek pamięci.
MGDavies,

2

Java 8 sposób (dzięki BufferedReader i Adamowi Bienowi )

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

Zauważ, że to rozwiązanie powoduje wyczyszczenie znaku powrotu karetki („\ r”) i może być nieodpowiednie.


4
To jest za String. OP prosi o byte[].
FrozenFire

To nie tylko \rmoże być problem. Ta metoda konwertuje bajty na znaki iz powrotem (przy użyciu domyślnego zestawu znaków dla InputStreamReader). Wszelkie bajty, które nie są prawidłowe w domyślnym kodowaniu znaków (powiedzmy -1 dla UTF-8 w systemie Linux), zostaną uszkodzone, potencjalnie nawet zmieniając liczbę bajtów.
seanf

Wydaje się, że to dobra odpowiedź, ale zorientowana na tekst. Kupujący strzeż się.
Wheezil

1

Próbowałem edytować odpowiedź @ numan z poprawką do zapisywania danych śmieci, ale edycja została odrzucona. Chociaż ten krótki fragment kodu nie jest niczym genialnym, nie widzę innej lepszej odpowiedzi. Oto, co ma dla mnie największy sens:

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 nie musi być zamknięty. konstrukcje try / wreszcie zostały pominięte ze względu na czytelność


1

Zobacz InputStream.available()dokumentację:

Szczególnie ważne jest, aby zdawać sobie sprawę, że nie wolno używać tej metody do określania rozmiaru kontenera i zakładać, że można odczytać cały strumień bez konieczności zmiany rozmiaru kontenera. Tacy wywołujący powinni prawdopodobnie zapisać wszystko, co czytają, w ByteArrayOutputStream i przekonwertować to na tablicę bajtów. Alternatywnie, jeśli czytasz z pliku, File.length zwraca bieżącą długość pliku (choć zakładanie, że długość pliku nie może się zmienić, może być niepoprawna, czytanie pliku jest z natury ryzykowne).


1

Zawiń go w DataInputStream, jeśli z jakiegoś powodu jest on poza tabelą, po prostu użyj read, aby młotkować, aż da ci -1 lub cały żądany blok.

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

Widzimy pewne opóźnienie dla kilku transakcji AWS, podczas konwertowania obiektu S3 na ByteArray.

Uwaga: Obiekt S3 jest dokumentem PDF (maksymalny rozmiar to 3 mb).

Używamy opcji nr 1 (org.apache.commons.io.IOUtils) do konwersji obiektu S3 na ByteArray. Zauważyliśmy, że S3 udostępnia wbudowaną metodę IOUtils do konwersji obiektu S3 na ByteArray, prosimy o potwierdzenie najlepszego sposobu konwersji obiektu S3 na ByteArray, aby uniknąć opóźnienia.

Opcja 1:

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

Opcja 2:

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

Daj mi również znać, jeśli mamy inny lepszy sposób na konwersję obiektu s3 na bytearray


0

Drugi przypadek uzyskania poprawnej tablicy bajtów przez strumień, po wysłaniu żądania do serwera i oczekiwaniu na odpowiedź.

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

Robisz dodatkową kopię, jeśli używasz ByteArrayOutputStream. Jeśli znasz długość strumienia, zanim zaczniesz go czytać (np. InputStream to tak naprawdę FileInputStream i możesz wywołać file.length () w pliku, lub InputStream to pozycja zipfile InputStream i możesz wywołać zipEntry. length ()), wtedy znacznie lepiej jest pisać bezpośrednio do tablicy byte [] - zużywa połowę pamięci i oszczędza czas.

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

Uwaga: ostatni wiersz powyżej dotyczy obcinania plików podczas odczytywania strumienia, jeśli trzeba skorzystać z tej możliwości, ale jeśli plik wydłuża się podczas odczytywania strumienia, zawartość tablicy byte [] nie zostanie przedłużona aby uwzględnić nową zawartość pliku, tablica zostanie po prostu obcięta do starej długości inputStreamLength .


0

Używam tego.

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
Dodaj wyjaśnienie wraz z odpowiedzią, w jaki sposób ta odpowiedź pomaga OP w rozwiązaniu bieżącego problemu
ρяσсρѕя K

0

To jest moja wersja kopiuj-wklej:

@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
Ten fragment kodu może rozwiązać pytanie, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
Ferrybig

0

Java 7 i nowsze wersje:

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

20
sun.misc.IOUtilsto nie „Java 7”. Jest to zastrzeżona, specyficzna dla implementacji klasa, która może nie być obecna w innych implementacjach JRE i może zniknąć bez ostrzeżenia w jednej z kolejnych wydań.
Holger


0

Oto zoptymalizowana wersja, która stara się unikać kopiowania bajtów danych w jak największym stopniu:

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

Rozwiązanie w Kotlinie (oczywiście będzie działało również w Javie), które obejmuje oba przypadki, gdy znasz rozmiar lub nie:

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

Jeśli znasz rozmiar, oszczędza to podwójnego wykorzystania pamięci w porównaniu do innych rozwiązań (w krótkiej chwili, ale nadal może być przydatne). Jest tak, ponieważ musisz przeczytać cały strumień do końca, a następnie przekonwertować go na tablicę bajtów (podobną do ArrayList, którą konwertujesz na samą tablicę).

Na przykład, jeśli korzystasz z Androida i masz trochę Uri do obsługi, możesz spróbować uzyskać rozmiar za pomocą tego:

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

Zamykanie i opróżnianie bos to marnowanie kliknięć na klawiaturze. Zamknięcie strumienia wejściowego może pomóc. Czytanie jednego bajtu na raz jest nieefektywne. Zobacz odpowiedź numana.
akostadinov,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.