0

I am writing a GWT service servlet, instance of RemoteServiceServlet, and given the fileID by the client, the goal is to fetch file from DB and push it to the client. The servlet returns any error message as String object.

What am I doing wrong?

I'm researching this on the net and some people claim that it can not be done from the RemoteServiceServlet?!

In my case the server has to fetch a file from database first, then it can send it to the client, so I have to prepare it first. Is there a way to "push" a download onto the client, or do I have to write a dedicated servlet just for the download? Can someone give me an example code how to do that?

Thanks!

This is the code I have so far and the exception it generates:

public class MyServiceImpl extends RemoteServiceServlet implements MyService {
    @Override
    public String getFile(Long fileId) {
        String errors = null;
        File file = fetchFileByID(fileId);
        try {
            if (file != null && file.exists()) {
                String name = fileLink.getName();
                HttpServletResponse response = getThreadLocalResponse();
                long fileLen = file.length();
                if (fileLen > Integer.MAX_VALUE)
                    throw new IllegalStateException("File too large, cannot download using the browser.");
                int length = (int) fileLen;
                // do not cache
                response.setHeader("Expires", "0");  
                response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");  
                response.setHeader("Pragma", "public");
                // content length is needed for MSIE
                response.setContentLength(length);
                response.setHeader("Content-Type", "application/octet-stream;");
                response.setHeader("Content-Disposition", "attachment;filename=\"" + name + "\"");
                OutputStream os = response.getOutputStream();
                FileInputStream is = new FileInputStream(file);
                BufferedInputStream buf = new BufferedInputStream(is);

                int readBytes=0;
                while((readBytes=buf.read())!=-1) {
                      os.write(readBytes);
                }   
                os.flush();
                buf.close();
            } else {
                errors = "File not found.";
            }
        } catch (IOException e) {
            errors = e.getMessage();
        } finally {
            if (file != null)
                file.delete();
        }
        return errors;
    }
}

Server generates an exception:

Starting Jetty on port 8888
   [WARN] /saluweb/SaluService
java.lang.RuntimeException: Unable to report failure
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doUnexpectedFailure(AbstractRemoteServiceServlet.java:107)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:67)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:686)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:501)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:557)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:428)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
    at org.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:68)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
    at org.eclipse.jetty.server.Server.handle(Server.java:370)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:489)
    at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:960)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1021)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:668)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Closed
    at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:140)
    at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:117)
    at com.google.gwt.user.server.rpc.RPCServletUtils.writeResponse(RPCServletUtils.java:375)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.writeResponse(RemoteServiceServlet.java:460)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:313)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
... 27 more
Marcin
  • 2,399
  • 3
  • 17
  • 15
  • Have you tried to read out the file directly and then test your servlet? Maybe your database access takes to long? In a application (I know) the file is first put into a byte array and then out.write, out.flush and out.close is called. And it works. I also can recommend Mockrunner (for Servlets) for testing, works great with GWT. – Akkusativobjekt Mar 29 '16 at 09:45
  • 1
    Have a look at [this question](http://stackoverflow.com/questions/2822667/download-dynamic-file-with-gwt). You can't return stream binary data from an RPC call, it has to be done from a standard servlet. – Baz Mar 29 '16 at 10:40
  • Thank you Baz, this lead me to a solution :) via returning a filename, and client side calling a dedicated download servlet. Thanks! – Marcin Mar 29 '16 at 15:00

1 Answers1

0

I found out from Sripathi Krishnan that you cannot stream binary data file in the RPC call -see this post https://stackoverflow.com/a/2823184/1431879

The solution was to follow the suggestion from SSR and implement Download Servlet - see this post https://stackoverflow.com/a/13739960/1431879

This however created a popup which was blocked by browsers. But having a file ready on the server and URL already prepared, my RPC returns the filename and client uses it to request direct download from a dedicated servlet. I created an iframe for downloads. I found an excellent post by Thad how to do that here http://grokbase.com/p/gg/google-web-toolkit/13264b94q6/how-to-download-a-file-from-server-without-browser-opening-new-window

Here is a direct quote from his post:

I have a number of places where the user can download a servlet generated file. I handle this generically with several steps

1) In my app's HTML, I put an IFRAME

2) In my app's EntryPoint, I declare

private static final String DOWNLOAD_IFRAME = "__gwt_downloadFrame"; private static Frame downloadFrame; ...

Wrap the IFRAME:

@Override public void onModuleLoad() { downloadFrame = Frame.wrap(Document.get().getElementById(DOWNLOAD_IFRAME)); ...

Provide method for downloading

public static void downloadURL(String url) { downloadFrame.setUrl(url); }

3) Whenever a download is ready, say in the onSuccess() of an RPC call

MyApp.downloadURL(url);

Community
  • 1
  • 1
Marcin
  • 2,399
  • 3
  • 17
  • 15