28

我一直在写一个小应用程序,它可以让人们上传和下载文件给我。我已经向这个应用程序添加了一个 Web 服务,以这种方式提供上传/下载功能,但我不太确定我的实现将如何处理大文件。

目前上传和下载方法的定义如下所示(使用 Apache CXF 编写):

boolean uploadFile(@WebParam(name = "username") String username,
    @WebParam(name = "password") String password,
    @WebParam(name = "filename") String filename,
    @WebParam(name = "fileContents") byte[] fileContents)
    throws UploadException, LoginException;

byte[] downloadFile(@WebParam(name = "username") String username,
    @WebParam(name = "password") String password,
    @WebParam(name = "filename") String filename) throws DownloadException,
    LoginException;

所以文件被上传和下载为一个字节数组。但是,如果我有一个大小愚蠢的文件(例如 1GB),这肯定会尝试将所有这些信息放入内存中并使我的服务崩溃。

所以我的问题是 - 是否可以返回某种流?我想这不会非常独立于操作系统。虽然我知道 Web 服务背后的理论,但我仍然需要了解一些实际信息。

为任何输入干杯,李

4

12 回答 12

14

是的,Metro 可以。请参阅大型附件示例,它看起来像您想要的那样。

JAX-WS RI 支持以流方式发送和接收大型附件。

  • 在编程模型中使用 MTOM 和 DataHandler。
  • 将 DataHandler 转换为 StreamingDataHandler 并使用其方法。
  • 确保调用 StreamingDataHandler.close() 并关闭 StreamingDataHandler.readOnce() 流。
  • 在客户端启用 HTTP 分块。
于 2008-09-25T11:02:25.617 回答
6

Stephen Denne有一个满足您要求的 Metro 实现。在简要说明为什么会出现这种情况后,我在下面提供了答案。

大多数使用 HTTP 作为消息协议构建的 Web 服务实现都与 REST 兼容,因为它们只允许简单的发送-接收模式,仅此而已。这极大地提高了互操作性,因为所有各种平台都可以理解这种简单的架构(例如,Java Web 服务与 .NET Web 服务通信)。

如果你想保持这一点,你可以提供分块。

boolean uploadFile(String username, String password, String fileName, int currentChunk, int totalChunks, byte[] chunk);

如果您没有以正确的顺序获得块(或者您可以只要求块以正确的顺序出现),这将需要一些步法,但这可能很容易实现。

于 2008-09-25T10:59:16.950 回答
3

当您使用标准化的 Web 服务时,发送方和接收方确实依赖于从一方发送到另一方的 XML 数据的完整性。这意味着 Web 服务请求和应答仅在发送最后一个标签时才完成。考虑到这一点,Web 服务不能被视为流。

这是合乎逻辑的,因为标准化的 Web 服务确实依赖于 http 协议。那个是“无状态的”,会说它的工作方式类似于“打开连接......发送请求......接收数据......关闭请求”。无论如何,连接将在最后关闭。所以像流这样的东西不打算在这里使用。或者他在 http 之上分层(如 Web 服务)。

很抱歉,但据我所知,不可能在网络服务中进行流式传输。更糟糕的是:根据 Web 服务的实现/配置,byte[] - 数据可能会被转换为 Base64 而不是 CDATA-tag,并且请求可能会变得更加臃肿。

PS:是的,正如其他人所写,“chuinking”是可能的。但这不是流式传输;-) - 无论如何,它可能会对您有所帮助。

于 2008-09-25T11:05:41.693 回答
1

对于 WCF,我认为可以将消息上的成员定义为流并适当地设置绑定 - 我已经看到 wcf 与 Java Web 服务对话的这项工作。

您需要在 httpTransport 配置中设置 transferMode="StreamedResponse" 并使用 mtomMessageEncoding(需要在配置中使用自定义绑定部分)。

我认为一个限制是,如果你想流式传输,你只能拥有一个消息正文成员(哪种有意义)。

于 2008-09-25T11:59:27.740 回答
1

Apache CXF支持发送和接收流。

于 2009-12-23T11:21:10.403 回答
1

对于那些认为不可能提供流式 Web 服务的人,我不想打破它,但实际上,所有 http 请求都是基于流的。每个对网站执行 GET 的浏览器都是基于流的。对 Web 服务的每次调用都是基于流的。是的,所有。我们在实现服务或页面的级别上没有注意到这一点,因为较低级别的架构正在为您处理 - 但它正在完成。

您是否曾经在浏览器中注意到有时可能需要一段时间才能获取一个页面 - 浏览器只是不停地显示沙漏?那是因为浏览器正在等待流。

流是必须在实际数据之前发送 mime/types 的原因——它只是一个字节流到浏览器,如果你不先告诉它它是什么,它就无法识别照片。这也是为什么您必须在发送之前传递二进制文件的大小 - 浏览器将无法判断图像在哪里停止并且页面再次拾取。

对客户端来说,这只是一个字节流。如果您想自己证明这一点,只需在处理请求的任何时候获取输出流并关闭()它。你会炸毁一切。浏览器将立即停止显示沙漏,并显示“找不到”或“服务器连接重置”或其他类似消息。

很多人不知道所有这些东西都是基于流的,这表明有多少东西已经分层。有些人会说太多的话——我就是其中之一。

祝你好运,快乐发展——放松肩膀!

于 2012-03-14T16:12:02.803 回答
0

一种方法是添加一个uploadFileChunk (byte[] chunkData, int size, int offset, int totalSize) 方法(或类似的方法),该方法上传部分文件,然后服务器将其写入磁盘。

于 2008-09-25T11:02:25.523 回答
0

请记住,Web 服务请求基本上可以归结为单个 HTTP POST。

如果您查看 .NET 中的 .ASMX 文件的输出,它会准确显示 POST 请求和响应的样子。

正如@Guvante 所提到的,分块将是最接近你想要的东西。

我想您可以实现自己的 Web 客户端代码来处理 TCP/IP 并将内容流式传输到您的应用程序中,但这至少可以说是复杂的。

于 2008-09-25T11:06:13.720 回答
0

我认为使用简单的servlet来完成这项任务会更容易,或者有什么理由不能使用 servlet?

例如,您可以使用Commons开源库。

于 2008-09-25T11:13:38.913 回答
0

Java的RMIIO库提供了跨 RMI 处理 RemoteInputStream - 我们只需要 RMI,尽管您应该能够调整代码以在其他类型的 RMI 上工作。这可能对您有所帮助——特别是如果您可以在用户端拥有一个小型应用程序。开发该库的明确目的是能够限制推送到服务器的数据大小,以避免您描述的那种情况 - 通过填满 ram 或磁盘有效地进行 DOS 攻击。

使用 RMIIO 库,服务器端可以决定它愿意提取多少数据,使用 HTTP PUT 和 POST,客户端可以做出决定,包括它推送的速率。

于 2008-09-25T11:59:14.810 回答
0

是的,网络服务可以进行流式传输。我使用 Apache Axis2 和 MTOM 创建了一个 Web 服务,以支持从 XML 呈现 PDF 文档。由于生成的文件可能非常大,因此流式传输很重要,因为我们不想将其全部保存在内存中。查看 Oracle 关于流式 SOAP 附件的文档。

或者,您可以自己做,tomcat 将创建 Chunked 标头。这是流式传输的弹簧控制器功能的示例。

 @RequestMapping(value = "/stream")
        public void hellostreamer(HttpServletRequest request, HttpServletResponse response) throws CopyStreamException, IOException  
{

            response.setContentType("text/xml");
            OutputStreamWriter writer = new OutputStreamWriter (response.getOutputStream());
            writer.write("this is streaming");
            writer.close();

    }
于 2011-08-08T15:39:23.240 回答
0

“处理 TCP/IP 并将内容流式传输到您的应用程序中”实际上并不难。试试这个...

class MyServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    {
        response.getOutputStream().println("Hello World!");
    }
}

这就是它的全部。在上面的代码中,您已经响应了从浏览器发送的 HTTP GET 请求,并向该浏览器返回了文本“Hello World!”。

请记住“Hello World!” 不是有效的 HTML,因此您最终可能会在浏览器上遇到错误,但这确实是它的全部。

祝您发展顺利!

罗德尼

于 2012-04-02T19:02:16.930 回答