Frage OutputStream OutOfMemoryError beim Senden von HTTP


Ich versuche, eine große Video / Image-Datei aus dem lokalen Dateisystem in einen HTTP-Pfad zu veröffentlichen, aber ich habe nach einiger Zeit einen Speicherfehler ...

Hier ist der Code

public boolean publishFile(URI publishTo, String localPath) throws Exception {
    InputStream istream = null;
    OutputStream ostream = null;
    boolean isPublishSuccess = false;

    URL url = makeURL(publishTo.getHost(), this.port, publishTo.getPath());
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();


    if (conn != null) {

        try {

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("PUT");
            istream = new FileInputStream(localPath);
            ostream = conn.getOutputStream();

            int n;
            byte[] buf = new byte[4096];
            while ((n = istream.read(buf, 0, buf.length)) > 0) {
                ostream.write(buf, 0, n); //<--- ERROR happens on this line.......???
            }

            int rc = conn.getResponseCode();

            if (rc == 201) {
                isPublishSuccess = true;
            }

        } catch (Exception ex) {
            log.error(ex);
        } finally {
            if (ostream != null) {
                ostream.close();
            }

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

    return isPublishSuccess;

}

HEre ist der Fehler, den ich bekomme ...

Exception in thread "Thread-8773" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2786)
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
    at sun.net.www.http.PosterOutputStream.write(PosterOutputStream.java:61)
    at com.test.HTTPClient.publishFile(HTTPClient.java:110)
    at com.test.HttpFileTransport.put(HttpFileTransport.java:97)

10
2018-01-17 18:18


Ursprung


Antworten:


Das HttpUrlConnection puffert die Daten so, dass sie die Content-Length Kopfzeile (per HTTP-Spezifikation).

Eine Alternative, wenn Ihr Zielserver dies unterstützt, ist die Verwendung von "chunked"Übertragungen. Dies wird nur einen kleinen Teil der Daten auf einmal puffern. Jedoch unterstützen nicht alle Dienste es (Amazon S3, zum Beispiel, nicht).

Eine andere Alternative (und eine bessere) ist zu verwenden Jakarta HttpClient. Sie können die "Entität" in einer Anforderung aus einer Datei festlegen, und der Verbindungscode wird Anforderungsheader entsprechend festlegen.


Bearbeiten: Nr kommentiert, dass das OP anrufen könnte HttpURLConnection.setFixedLengthStreamingMode(long length). Ich war mir dieser Methode nicht bewusst; Es wurde in 1.5 hinzugefügt und ich habe diese Klasse seither nicht mehr benutzt.

Wie auch immer, ich immer noch schlagen Sie vor, Jakarta HttpClient zu verwenden, aus dem einfachen Grund, dass es die Menge an Code reduziert, die das OP erhalten muss. Code, der vordefiniert ist, aber immer noch das Potenzial für Fehler hat:

  • Das OP behandelt die Schleife korrekt, um zwischen Eingang und Ausgang zu kopieren. Normalerweise, wenn ich ein Beispiel dafür sehe, überprüft das Poster entweder die zurückgegebene Puffergröße nicht oder behält die Puffer neu zu. Herzlichen Glückwunsch, aber Sie müssen jetzt sicherstellen, dass Ihre Nachfolger so sorgfältig behandelt werden.
  • Die Ausnahmebehandlung ist nicht ganz so gut. Ja, das OP erinnert sich daran, die Verbindungen in a zu schließen finally blockieren, und noch einmal, herzlichen Glückwunsch dazu. Außer dass entweder der close() Anrufe könnten werfen IOExceptionden anderen davon abzuhalten, etwas auszuführen. Und die Methode als Ganzes wirft Exception, damit der Compiler nicht hilft, ähnliche Fehler zu finden.
  • Ich zähle 31 Codezeilen, um die Antwort einzurichten und auszuführen (mit Ausnahme der Antwortcodeüberprüfung und der URL-Berechnung, aber einschließlich try / catch / finally). Mit HttpClient würde dies irgendwo im Bereich von einem halben Dutzend LOC liegen.

Selbst wenn der OP diesen Code perfekt geschrieben und ihn in ähnliche Methoden wie in Jakarta Commons IO umstrukturiert hätte, er / sie sollte das nicht tun. Dieser Code wurde von anderen geschrieben und getestet. Ich weiß, dass es eine Verschwendung ist meine Zeit, es umzuschreiben und zu vermuten, dass es auch eine Verschwendung der OP-Zeit ist.


13
2018-01-17 18:25



conn.setFixedLengthStreamingMode((int) new File(localpath).length());

Und zum Puffern könntest du deine Streams in die BufferedOutputStream und BufferedInputStream

Ein gutes Beispiel für chunked uploading finden Sie dort: gdata-java-Client


3
2017-10-25 17:15



Das Problem besteht darin, dass die HttpURLConnection-Klasse ein Bytearray zum Speichern Ihrer Daten verwendet. Vermutlich nimmt dieses Video mehr Speicherplatz in Anspruch als verfügbar. Sie haben hier einige Möglichkeiten:

  1. Erhöhen Sie den Speicher für Ihre Anwendung. Sie können die Option -Xmx1024m verwenden, um Ihrer Anwendung 1 GB Speicher zu geben. Dies erhöht die Menge an Daten, die Sie im Speicher speichern können.

  2. Wenn immer noch nicht genügend Arbeitsspeicher zur Verfügung steht, sollten Sie möglicherweise eine andere Bibliothek ausprobieren, um das Video hochzuladen, das die Daten nicht alle im Speicher auf einmal speichert. Der Apache Commons HttpClient hat eine solche Funktion. Sehen Sie diese Seite für weitere Informationen: http://hc.apache.org/httpclient-3.x/features.html. In diesem Abschnitt finden Sie Informationen zum Upload von großen Dateien in mehrere Teile: http://hc.apache.org/httpclient-3.x/methods/multipartpost.html


2
2018-01-17 18:26



Für etwas anderes als grundlegende GET-Operationen, das eingebaute java.net HTTP Zeug ist nicht sehr gut. Verwenden Apache Commons HttpClient ist dafür zu empfehlen. So können Sie viel intuitivere Sachen machen:

PutMethod put = new PutMethod(url);
put.setRequestEntity(new FileRequestEntity(localFile, contentType));
int responseCode = put.executeMethod();

Das ersetzt eine Menge Ihres Kessels.


2
2018-01-17 18:32



HttpsURLConnection # setChunkedStreamingMode (1024 * 1024 * 10); // 10 MB Chunk Dadurch wird sichergestellt, dass jede Datei (beliebiger Größe) ohne interne Pufferung über eine HTTPS-Verbindung gestreamt wird. Dies sollte verwendet werden, wenn die Dateigröße oder die Inhaltslänge unbekannt ist.


1
2018-04-27 16:15



Ihr Problem besteht darin, dass Sie versuchen, X-Video-Bytes in X / N-Bytes des RAM zu reparieren, wenn N> 1 ist.

Entweder müssen Sie das Video in einen kleineren Puffer einlesen und es aufschreiben, oder Sie verkleinern die Datei oder erhöhen den für Ihren Prozess verfügbaren Speicher.

Überprüfen Sie Ihre Heap-Größe. Sie können -Xmx verwenden, um es zu erhöhen, wenn Sie den Standard gewählt haben.


-1
2018-01-17 18:25