使用 GWT RemoteServiceServlet 下载文件

Posted

技术标签:

【中文标题】使用 GWT RemoteServiceServlet 下载文件【英文标题】:Download file with GWT RemoteServiceServlet 【发布时间】:2016-03-28 19:54:29 【问题描述】:

我正在编写一个 GWT 服务 servlet,RemoteServiceServlet 的实例,并给定客户端的文件 ID,目标是从数据库中获取文件并将其推送到客户端。 servlet 将任何错误消息作为 String 对象返回。

我做错了什么?

我正在网上研究这个,有人声称它不能从 RemoteServiceServlet 完成?!

在我的情况下,服务器必须先从数据库中获取文件,然后才能将其发送到客户端,所以我必须先准备它。有没有办法将下载“推送”到客户端,还是我必须为下载编写一个专用的 servlet?谁能给我一个示例代码如何做到这一点?

谢谢!

这是我目前拥有的代码及其产生的异常:

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;
    

服务器产生异常:

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

【问题讨论】:

你试过直接读出文件然后测试你的servlet吗?也许您的数据库访问需要很长时间?在应用程序(我知道)中,文件首先放入字节数组,然后调用 out.write、out.flush 和 out.close。它有效。我还可以推荐 Mockrunner(用于 Servlets)进行测试,与 GWT 配合得很好。 看看this question。您不能从 RPC 调用返回流二进制数据,它必须从标准 servlet 完成。 谢谢 Baz,这让我找到了一个解决方案 :) 通过返回文件名和客户端调用专用的下载 servlet。谢谢! 【参考方案1】:

我从 Sripathi Krishnan 发现您无法在 RPC 调用中流式传输二进制数据文件 - 请参阅此帖子 https://***.com/a/2823184/1431879

解决方案是遵循 s-s-r 的建议并实施 Download Servlet - 请参阅此帖子 https://***.com/a/13739960/1431879

然而,这创建了一个被浏览器阻止的弹出窗口。但是在服务器上准备好文件并且 URL 已经准备好,我的 RPC 返回文件名,客户端使用它来请求从专用 servlet 直接下载。我创建了一个 iframe 供下载。我在这里找到了 Thad 的一篇很棒的帖子 http://grokbase.com/p/gg/google-web-toolkit/13264b94q6/how-to-download-a-file-from-server-without-browser-opening-new-window

这是他帖子的直接引述:

我有很多地方可以让用户下载生成的 servlet 文件。我通常通过几个步骤来处理这个问题

1) 在我的应用程序的 html 中,我放置了一个 IFRAME

2) 在我的应用的入口点中,我声明

私有静态最终字符串 DOWNLOAD_IFRAME = "__gwt_downloadFrame"; 私有静态帧下载帧; ...

包装 IFRAME:

@覆盖 公共无效 onModuleLoad() 下载帧 = Frame.wrap(Document.get().getElementById(DOWNLOAD_IFRAME)); ...

提供下载方法

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

3) 每当下载准备好时,在 RPC 调用的 onSuccess() 中说

MyApp.downloadURL(url);

【讨论】:

以上是关于使用 GWT RemoteServiceServlet 下载文件的主要内容,如果未能解决你的问题,请参考以下文章

GWT 错误:“类在 Gin 中使用,但在 GWT 客户端代码中不可用”

GWT RPC 未生成正确的 gwt.rpc 文件

GWT Spring 安全集成(纯 GWT,无 JSP)

可以在 AIX 上使用 google web toolkit(GWT) 吗?

如何使用 GWT 序列化流工厂

GWT 的各种异步设施