使用 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 客户端代码中不可用”