Frage

Ich habe eine Input einer Datei und ich verwende Apache poi Komponenten aus es wie folgt zu lesen:

POIFSFileSystem fileSystem = new POIFSFileSystem(inputStream);

Das Problem ist, dass ich die gleichen Strom mehrmals und der POIFSFileSystem schließt den Strom nach Gebrauch verwenden müssen.

Was ist der beste Weg, um die Daten aus dem Eingangsstrom zwischenzuspeichern und dann mehr Input dienen Ströme zu verschiedenen POIFSFileSystem?

EDIT 1:

Mit dem Cache sollte ich Speicher für die spätere Verwendung, nicht als eine Möglichkeit, um die Anwendung zu Speedup. Auch ist es besser, nur den Eingangsstrom in einem Array oder String zu lesen und dann erstellen Eingangsströme für jeden Einsatz?

EDIT 2:

Es tut uns Leid, die Frage erneut zu öffnen, aber die Bedingungen sind etwas anders, wenn innerhalb Desktop- und Web-Anwendung arbeiten. Vor allem der Input i von der org.apache.commons.fileupload.FileItem app in meinem Kater Web erhalten hat Markierungen nicht so unterstützen können nicht zurückgesetzt werden.

Zweitens, ich möchte in der Lage sein, die Datei im Speicher für schnelleren Zugang und weniger io Probleme zu halten, wenn sie mit Dateien zu tun.

War es hilfreich?

Lösung

Sie können dekorieren Inputstream übergeben werden POIFSFileSystem mit einer Version, dass, wenn close () mit Reset reagieren () aufgerufen wird:

class ResetOnCloseInputStream extends InputStream {

    private final InputStream decorated;

    public ResetOnCloseInputStream(InputStream anInputStream) {
        if (!anInputStream.markSupported()) {
            throw new IllegalArgumentException("marking not supported");
        }

        anInputStream.mark( 1 << 24); // magic constant: BEWARE
        decorated = anInputStream;
    }

    @Override
    public void close() throws IOException {
        decorated.reset();
    }

    @Override
    public int read() throws IOException {
        return decorated.read();
    }
}

Testfall

static void closeAfterInputStreamIsConsumed(InputStream is)
        throws IOException {
    int r;

    while ((r = is.read()) != -1) {
        System.out.println(r);
    }

    is.close();
    System.out.println("=========");

}

public static void main(String[] args) throws IOException {
    InputStream is = new ByteArrayInputStream("sample".getBytes());
    ResetOnCloseInputStream decoratedIs = new ResetOnCloseInputStream(is);
    closeAfterInputStreamIsConsumed(decoratedIs);
    closeAfterInputStreamIsConsumed(decoratedIs);
    closeAfterInputStreamIsConsumed(is);
}

EDIT 2

Sie können die gesamte Datei in einem byte [] (schlürfen Modus) lesen sie dann zu einem ByteArrayInputStream vorbei

Andere Tipps

Versuchen BufferedInputStream, die Marke ergänzt und Funktionalität auf einem anderen Eingabestrom zurückzusetzen, und nur seine schließen Methode außer Kraft setzen:

public class UnclosableBufferedInputStream extends BufferedInputStream {

    public UnclosableBufferedInputStream(InputStream in) {
        super(in);
        super.mark(Integer.MAX_VALUE);
    }

    @Override
    public void close() throws IOException {
        super.reset();
    }
}

So:

UnclosableBufferedInputStream  bis = new UnclosableBufferedInputStream (inputStream);

und verwenden bis überall dort, wo vor input verwendet wurde.

Das funktioniert richtig:

byte[] bytes = getBytes(inputStream);
POIFSFileSystem fileSystem = new POIFSFileSystem(new ByteArrayInputStream(bytes));

wo getBytes ist wie folgt:

private static byte[] getBytes(InputStream is) throws IOException {
    byte[] buffer = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
int n;
baos.reset();

while ((n = is.read(buffer, 0, buffer.length)) != -1) {
      baos.write(buffer, 0, n);
    }

   return baos.toByteArray();
 }

Verwenden Sie die folgende Implementierung für mehr individuelle Nutzung -

public class ReusableBufferedInputStream extends BufferedInputStream
{

    private int totalUse;
    private int used;

    public ReusableBufferedInputStream(InputStream in, Integer totalUse)
    {
        super(in);
        if (totalUse > 1)
        {
            super.mark(Integer.MAX_VALUE);
            this.totalUse = totalUse;
            this.used = 1;
        }
        else
        {
            this.totalUse = 1;
            this.used = 1;
        }
    }

    @Override
    public void close() throws IOException
    {
        if (used < totalUse)
        {
            super.reset();
            ++used;
        }
        else
        {
            super.close();
        }
    }
}

Was genau meinst du mit „Cache“? Haben Sie die verschiedenen POIFSFileSystem am Anfang des Stroms zu starten? Wenn ja, gibt es absolut keinen Sinn, etwas das Caching in Ihrem Java-Code; es wird durch das OS durchgeführt werden, öffnen Sie einfach einen neuen Stream.

Oder haben Sie an dem Punkt weiter zu lesen wan, wo der erste POIFSFileSystem gestoppt? Das ist nicht Caching, und es ist sehr schwer zu tun. Die einzige Möglichkeit, die ich denken kann, wenn man den Strom nicht vermeiden kann, geschlossen zu werden wäre eine dünne Hülle zu schreiben, der zählt, wie viele Bytes gelesen wurde und dann einen neuen Stream öffnen und überspringt, dass viele Bytes. Aber das könnte scheitern, wenn POIFSFileSystem intern so etwas wie ein BufferedInputStream verwendet.

Wenn die Datei nicht so groß ist, es in eine byte[] Array lesen und geben POI eine ByteArrayInputStream aus diesem Array erstellt.

Wenn die Datei groß ist, dann sollten Sie sich nicht, da das Betriebssystem wird für Sie am besten das Caching tun, wie es kann.

[EDIT] Verwenden Sie Apache commons-io die Datei in einen Byte-Array in einem lesen effizienter Weg. Verwenden Sie int read() nicht, da es die Datei Byte für Byte liest, was sehr langsam!

Wenn Sie es selbst tun, verwenden Sie ein File Objekt die Länge zu erhalten, erstellen Sie das Array und die eine Schleife, die Bytes aus der Datei liest. Sie müssen Schleife, da read(byte[], int offset, int len) können weniger als len Bytes gelesen (und in der Regel der Fall ist).

Dies ist, wie ich umgesetzt würde, sicher mit jedem Input verwendet werden:

  • schreiben Sie Ihre eigene Input Wrapper, wo Sie eine temporäre Datei erstellen den ursprünglichen Strominhalt spiegeln
  • Dump alles von dem ursprünglichen Eingangsstrom in diese temporäre Datei lesen
  • , wenn der Strom vollständig lesen Sie gespiegelt, alle Daten werden in die temporäre Datei
  • verwenden InputStream.reset zu wechseln (initialisieren), um den internen Strom zu einem Fileinputstream (mirrored_content_file)
  • Sie von nun an wird die Referenz des ursprünglichen Stroms lose (kann gesammelt werden)
  • fügen Sie eine neue Methode Release (), die die temporäre Datei entfernen wird und lassen Sie alle geöffneten Stream.
  • Sie können auch Release () aufrufen, von finalize sicher sein, die temporäre Datei Freiheit im Falle ist, dass Sie vergessen Release () aufrufen (die meiste Zeit sollten Sie vermeiden, mit finalize , rufen Sie immer eine Methode Objektressourcen freizugeben). finden Sie unter Warum würden Sie jemals implementieren finalize ()?
public static void main(String[] args) throws IOException {
    BufferedInputStream inputStream = new BufferedInputStream(IOUtils.toInputStream("Foobar"));
    inputStream.mark(Integer.MAX_VALUE);
    System.out.println(IOUtils.toString(inputStream));
    inputStream.reset();
    System.out.println(IOUtils.toString(inputStream));
}

Das funktioniert. IOUtils ist Teil der commons IO.

Diese Antwort Iterierten auf früheren 1 | 2 auf der Grundlage des BufferInputStream. Die wichtigsten Änderungen sind, dass es unendlich Wiederverwendung ermöglicht. Und übernimmt den ursprünglichen Quelle Eingangsstrom von Schließsystemressourcen frei-up. Ihr Betriebssystem definiert eine Grenze für diejenigen, und Sie wollen nicht, das Programm aus Datei-Handles ( Das ist auch, warum Sie immer ‚verbraucht‘ sollten Antworten zum Beispiel mit der Apache EntityUtils.consumeQuietly() ) laufen. Bearbeiten Aktualisiert den Code für Gready Verbraucher zu behandeln, die read(buffer, offset, length) verwenden, in diesem Fall kann es, dass BufferedInputStream passieren versucht, hart an der Quelle zu suchen, dieser Code schützt gegen diese Verwendung.

public class CachingInputStream extends BufferedInputStream {    
    public CachingInputStream(InputStream source) {
        super(new PostCloseProtection(source));
        super.mark(Integer.MAX_VALUE);
    }

    @Override
    public synchronized void close() throws IOException {
        if (!((PostCloseProtection) in).decoratedClosed) {
            in.close();
        }
        super.reset();
    }

    private static class PostCloseProtection extends InputStream {
        private volatile boolean decoratedClosed = false;
        private final InputStream source;

        public PostCloseProtection(InputStream source) {
            this.source = source;
        }

        @Override
        public int read() throws IOException {
            return decoratedClosed ? -1 : source.read();
        }

        @Override
        public int read(byte[] b) throws IOException {
            return decoratedClosed ? -1 : source.read(b);
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            return decoratedClosed ? -1 : source.read(b, off, len);
        }

        @Override
        public long skip(long n) throws IOException {
            return decoratedClosed ? 0 : source.skip(n);
        }

        @Override
        public int available() throws IOException {
            return source.available();
        }

        @Override
        public void close() throws IOException {
            decoratedClosed = true;
            source.close();
        }

        @Override
        public void mark(int readLimit) {
            source.mark(readLimit);
        }

        @Override
        public void reset() throws IOException {
            source.reset();
        }

        @Override
        public boolean markSupported() {
            return source.markSupported();
        }
    }
}

Zur Wiederverwendung es schließen Sie es sich, wenn es nicht war.

Eine Einschränkung ist aber, dass, wenn der Strom geschlossen wird, bevor der gesamte Inhalt des ursprünglichen Stroms gelesen wurde, dann ist dieser Dekorateur unvollständige Daten hat, so stellen Sie sicher, dass der gesamte Strom vor dem Schließen gelesen wird.

Ich füge nur meine Lösung hier, da dies für mich funktioniert. Es ist im Grunde eine Kombination der beiden Top-Antworten:)

    private String convertStreamToString(InputStream is) {
    Writer w = new StringWriter();
    char[] buf = new char[1024];
    Reader r;
    is.mark(1 << 24);
    try {
        r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        int n;
        while ((n=r.read(buf)) != -1) {
            w.write(buf, 0, n);
        }
        is.reset();
    } catch(UnsupportedEncodingException e) {
        Logger.debug(this.getClass(), "Cannot convert stream to string.", e);
    } catch(IOException e) {
        Logger.debug(this.getClass(), "Cannot convert stream to string.", e);
    }
    return w.toString();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top