2010-02-19 20 views
30

Sto cercando di leggere un'immagine da un URL (con il pacchetto java java.net.URL) in un byte []. "Tutto" funziona bene, tranne per il fatto che il contenuto non viene letto direttamente dallo stream (l'immagine è corrotta, non contiene tutti i dati dell'immagine) ... L'array di byte viene mantenuto in un database (BLOB). Io davvero non so che cosa l'approccio corretto è, forse si può darmi un suggerimento :)java.net.URL read stream to byte []

Questo è il mio primo approccio (codice formattato, informazioni inutili rimossi ...):

URL u = new URL("http://localhost:8080/images/anImage.jpg"); 
int contentLength = u.openConnection().getContentLength(); 
Inputstream openStream = u.openStream(); 
byte[] binaryData = new byte[contentLength]; 
openStream.read(binaryData); 
openStream.close(); 

mio secondo approccio era questo uno (come avrete capito il ContentLength viene recuperato in un altro modo):

URL u = new URL(content); 
openStream = u.openStream(); 
int contentLength = openStream.available(); 
byte[] binaryData = new byte[contentLength]; 
openStream.read(binaryData); 
openStream.close(); 

Entrambi risultato codice in un immagine corrotta ... ho già letto questo post from stackoverflow

risposta

51

Non è possibile garantire che la lunghezza del contenuto fornita sia effettivamente corretta. Provare qualcosa di simile al seguente:

ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
InputStream is = null; 
try { 
    is = url.openStream(); 
    byte[] byteChunk = new byte[4096]; // Or whatever size you want to read in at a time. 
    int n; 

    while ((n = is.read(byteChunk)) > 0) { 
    baos.write(byteChunk, 0, n); 
    } 
} 
catch (IOException e) { 
    System.err.printf ("Failed while reading bytes from %s: %s", url.toExternalForm(), e.getMessage()); 
    e.printStackTrace(); 
    // Perform any other exception handling that's appropriate. 
} 
finally { 
    if (is != null) { is.close(); } 
} 

Avrai quindi i dati di immagine in baos, da cui è possibile ottenere un array di byte chiamando baos.toByteArray().

Questo codice non è stato verificato (l'ho appena scritto nella casella di risposta), ma è un'approssimazione ragionevolmente vicina a quello che penso tu stia cercando.

+27

Per favore non scrivere mai un vuoto blocco, nemmeno in un esempio! Metti almeno 'e.printStackTrace()' lì! Gli esempi hanno la tendenza a diventare un codice di produzione e tutti dobbiamo lavorarci sopra in seguito. –

+2

Hai assolutamente ragione; Grazie per la segnalazione. Ho aggiunto una gestione delle eccezioni più significativa all'esempio. – RTBarnard

+0

Utilizzare http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toByteArray(java.io.InputStream). Questo renderà il codice molto più pulito. – Adi

1

La lunghezza del contenuto è solo un'intestazione HTTP. Non ci si può fidare di esso. Basta leggere tutto ciò che puoi dal flusso.

Disponibile è sicuramente sbagliato. È solo il numero di byte che possono essere letti senza bloccare.

Un altro problema è la gestione delle risorse. La chiusura del flusso deve avvenire in ogni caso. provare/prendere/finalmente lo farà.

+0

Thx per la risposta. Ometto il try/catch nel mio posting del codice. Ma come posso conoscere la lunghezza esatta del flusso? Devo allocare il byte [], quindi devo fornire una lunghezza. Assegnare una lunghezza fissa (ad esempio 1024) e la lettura da una posizione a un offset, controllare se il flusso contiene dati, copiare su un nuovo array, unire tutti i byte [] non potrebbe essere la soluzione migliore ... –

23

Semplicemente estendendo la risposta di Barnards a Commons-io. Risposta separata perché non riesco a formattare il codice nei commenti.

InputStream is = null; 
try { 
    is = url.openStream(); 
    byte[] imageBytes = IOUtils.toByteArray(is); 
} 
catch (IOException e) { 
    System.err.printf ("Failed while reading bytes from %s: %s", url.toExternalForm(), e.getMessage()); 
    e.printStackTrace(); 
    // Perform any other exception handling that's appropriate. 
} 
finally { 
    if (is != null) { is.close(); } 
} 

http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toByteArray(java.io.InputStream)

+0

Questa è un'altra bella soluzione, ma userò la prima (perché non includeremo troppo libs esterne) –

9
byte[] b = IOUtils.toByteArray((new URL()).openStream()); //idiom 

nota tuttavia, tale flusso non è chiuso nell'esempio precedente.

se si desidera una (76 caratteri) pezzo (utilizzando comuni codec) ...

byte[] b = Base64.encodeBase64(IOUtils.toByteArray((new URL()).openStream()), true); 
+6

-1 per una formattazione impropria e un po 'volgare – asgs

16

Ecco una soluzione pulita:

private byte[] downloadUrl(URL toDownload) { 
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 

    try { 
     byte[] chunk = new byte[4096]; 
     int bytesRead; 
     InputStream stream = toDownload.openStream(); 

     while ((bytesRead = stream.read(chunk)) > 0) { 
      outputStream.write(chunk, 0, bytesRead); 
     } 

    } catch (IOException e) { 
     e.printStackTrace(); 
     return null; 
    } 

    return outputStream.toByteArray(); 
} 
+5

Non dimenticare di chiamare il metodo close su' stream ' rilasciare le risorse utilizzate. –

7

Sono molto sorpreso che nessuno qui ha menzionato il problema della connessione e il timeout di lettura. Potrebbe accadere (specialmente su Android e/o con qualche crappy connettività di rete) che la richiesta si blocchi e attenda per sempre.

Il seguente codice (che utilizza anche Apache IO Commons) tiene conto di ciò e attende max.5 secondi fino a quando fallisce:

public static byte[] downloadFile(URL url) 
{ 
    try { 
     URLConnection conn = url.openConnection(); 
     conn.setConnectTimeout(5000); 
     conn.setReadTimeout(5000); 
     conn.connect(); 

     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     IOUtils.copy(conn.getInputStream(), baos); 

     return baos.toByteArray(); 
    } 
    catch (IOException e) 
    { 
     // Log error and return null, some default or throw a runtime exception 
    } 
}