2016-02-17 14 views
10

Ich versuche, eine PDF-Datei mit URLConnection herunterladen. So stelle ich das Verbindungsobjekt ein.PDF-Datei herunterladen mit BlockingQueue

URL serverUrl = new URL(url); 
urlConnection = (HttpURLConnection) serverUrl.openConnection(); 
urlConnection.setDoInput(true); 
urlConnection.setRequestMethod("GET"); 
urlConnection.setRequestProperty("Content-Type", "application/pdf"); 
urlConnection.setRequestProperty("ENCTYPE", "multipart/form-data"); 
String contentLength = urlConnection.getHeaderField("Content-Length"); 

Ich erhielt Eingangsstrom aus dem Verbindungsobjekt.

bufferedInputStream = new BufferedInputStream(urlConnection.getInputStream()); 

Und der Ausgabestrom, um den Dateiinhalt zu schreiben.

BlockingQueue wird erstellt, damit Threads, die Lese- und Schreibvorgänge ausführen, auf die Warteschlange zugreifen können.

final BlockingQueue<ByteArrayWrapper> blockingQueue = new ArrayBlockingQueue<ByteArrayWrapper>(MAX_VALUE,true); 
final byte[] dataBuffer = new byte[MAX_VALUE]; 

Jetzt erstellt Thread zum Lesen von Daten aus InputStream.

Thread readerThread = new Thread(new Runnable() { 
     @Override 
     public void run() { 
     try { 
      int count = 0; 
      while((count = bufferedInputStream.read(dataBuffer, 0, dataBuffer.length)) != -1) { 
       ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper(dataBuffer); 
       byteArrayWrapper.setBytesReadCount(count); 
       blockingQueue.put(byteArrayWrapper); 
      } 
      blockingQueue.put(null); //end of file 
      } catch(Exception e) { 
       e.printStackTrace(); 
      } finally { 
       try { 
       bufferedInputStream.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
}); 

Jetzt liest der Autorenthread diese Dateiinhalte.

Thread writerThread = new Thread(new Runnable() { 
     @Override 
     public void run() { 
     try { 
      while(true) { 
       ByteArrayWrapper byteWrapper = blockingQueue.take(); 
       if(null == byteWrapper) break; 
       bufferedOutputStream.write(byteWrapper.getBytesRead(), 0, byteWrapper.getBytesReadCount()); 
      } 
      bufferedOutputStream.flush(); 
     } catch(Exception e) { 
       e.printStackTrace(); 
     } finally { 
       try { 
       bufferedOutputStream.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
     } 
     } 
}); 

Schließlich werden Threads gestartet.

readerThread.start(); 
writerThread.start(); 

Theoretisch sollte es die Datei von InputStream lesen und in der Zieldatei speichern. In Wirklichkeit produziert es jedoch eine leere PDF-Datei. Zu einem anderen Zeitpunkt wird eine ungültige PDF-Format-Ausnahme angezeigt. Die Dateigröße stimmt mit der Inhaltslänge von InputStream überein. Gibt es etwas, das ich vermisse?

Antwort

5

Ich bin nicht vertraut mit ByteArrayWrapper. Hat es nur einen Verweis auf das Array, so?

public class ByteArrayBuffer { 
    final private byte[] data; 

    public ByteArrayBuffer(byte[] data) { 
     this.data = data; 
    } 

    public byte[] getBytesRead() { 
     return data; 
    } 

    /*...etc...*/ 
} 

Wenn ja. das wäre das Problem: Alle ByteArrayWrapper Objekte werden vom selben Array unterstützt. Das wird vom Schreiber wiederholt überschrieben. Auch wenn BlockingQueue die harte Arbeit des sicheren Publizierens jedes Objekts von einem Thread auf den anderen erledigt.

Die einfachste Lösung könnte darin bestehen, das ByteArrayWrapper effektiv unveränderlich zu machen, d. H. Es nicht ändern, nachdem es in einem anderen Thread veröffentlicht wurde. wäre am einfachsten, eine Kopie des Arrays auf dem Bau unter:

public ByteArrayWrapper(byte[] data) { 
    this.data = Arrays.copyOf(data, data.length); 
} 

Ein weiteres Problem ist, dass (siehe BlockingQueue docs) „Blocking nicht null Elemente akzeptiert“, und so der „Ende des Eingangs“ Sentinel-Wert nicht Arbeit. Ersetzen null mit einem

private static ByteArrayWrapper END = new ByteArrayWrapper(new byte[]{}); 

an den entsprechenden Stellen wird das in Ordnung bringen.

Durch diese Änderungen an einer Kopie des Codes konnte ich eine originalgetreue Kopie einer PDF-Datei abrufen.

+0

Ja, es tut.Sie haben Recht https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/03118a2/java/src/com/android/inputmethod/latin/ByteArrayWrapper.java – xAF