简体   繁体   中英

Restlet - StreamClosedException using StreamingOutput

I'm experiencing problems with my Restlet Resource. It uses TrueZip to build a subset of a Zip Archive and allows the user to download it.

// Create a Streaming Response Entity.
    final StreamingOutput stream = new StreamingOutput() {
        @Override
        public void write(final OutputStream output) {
                ZipBrowser.extract(source, path, output);
        }
    };
    LOGGER.debug("Download of Path {} with the length {} initiated", path, length);
    ResponseBuilder rb = Response.ok(stream);
    rb.header(HeaderConstants.HEADER_CONTENT_DISPOSITION, CONDISPOVALUE + fileName);
    rb.header(HeaderConstants.HEADER_CONTENT_LENGTH, length);
    return rb.build();

Even though it works I receive an annoying StreamClosedException. This error appears only when I try to download a subset and not the whole Zip Archive:

An exception occured writing the responseentity

    sun.net.httpserver.StreamClosedException
    at sun.net.httpserver.ChunkedOutputStream.flush(ChunkedOutputStream.java:156)
    at sun.net.httpserver.PlaceholderOutputStream.flush(ExchangeImpl.java:449)
    at org.restlet.engine.adapter.ServerCall.writeResponseBody(ServerCall.java:511)
    at org.restlet.engine.adapter.ServerCall.sendResponse(ServerCall.java:454)
    at org.restlet.engine.adapter.ServerAdapter.commit(ServerAdapter.java:187)
    at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:144)
    at org.restlet.engine.connector.HttpServerHelper$1.handle(HttpServerHelper.java:64)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
    at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
    at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
    at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:649)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)Unable to send error response

    java.io.IOException: headers already sent

    at sun.net.httpserver.ExchangeImpl.sendResponseHeaders(ExchangeImpl.java:204)
    at sun.net.httpserver.HttpExchangeImpl.sendResponseHeaders(HttpExchangeImpl.java:86)
    at org.restlet.engine.connector.HttpExchangeCall.writeResponseHead(HttpExchangeCall.java:148)
    at org.restlet.engine.adapter.ServerCall.sendResponse(ServerCall.java:450)
    at org.restlet.engine.adapter.ServerAdapter.commit(ServerAdapter.java:205)
    at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:144)
    at org.restlet.engine.connector.HttpServerHelper$1.handle(HttpServerHelper.java:64)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
    at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
    at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
    at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:649)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

This is caused by both the zip library and the Restlet Framework trying to close the OutputStream once they have finished.

I have experienced this in the past with other libraries and have managed to eliminate the exception by wrapping the OutputStream I pass to the zip library in a class which Overrides close() to do nothing. With all other methods delegated. This then allows Restlet to close the stream.

So that the line calling the Zip utility in your code becomes:

 ZipBrowser.extract(source, path, new WrappedOutputStream(output));

Where the WrappedOutputStream class is indicated below (delegating methods will need to be added).

import java.io.IOException;
import java.io.OutputStream;

public class WrappedOutputStream extends OutputStream {

    private final OutputStream delegate;

    public WrappedOutputStream(final OutputStream delegate) {
        this.delegate = delegate;
    }

    public void close() throws IOException {
        // Do Nothing to allow Restlet to close the underlying stream
    }

    // TODO Delegate other Outpt Stream methods.
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM