如何从与Jersey的多部分表单中读取具有相同名称的多个(文件)输入?

我已经成功开发了一项服务,我在其中阅读在泽西岛以多部分forms上传的文件。 这是我一直在做的非常简化的版本:

@POST @Path("FileCollection") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile(@FormDataParam("file") InputStream uploadedInputStream, @FormDataParam("file") FormDataContentDisposition fileDetail) throws IOException { //handle the file } 

这工作得很好,但我已经给了一个新的要求。 除了我上传的文件外,我还要处理任意数量的资源。 我们假设这些是图像文件。

我想我只是为客户端提供一个表单,其中包含一个文件输入,第一个图像的一个输入和一个允许向表单添加更多输入的按钮(使用AJAX或简单的JavaScript)。

 

因此,用户可以为表单添加更多图像输入,如下所示:

 

我希望阅读与集合同名的字段非常简单。 我在MVC .NET中使用文本输入成功完成了它,我认为在Jersey中不会更难。 事实certificate我错了。

找不到关于这个主题的教程,我开始尝试。

为了看看如何做到这一点,我把问题简化为简单的文本输入。

 
Multiple inputs with the same name

显然,我需要将某种集合作为我方法的参数。 这是我尝试过的,按集合类型分组。

排列

起初,我检查了Jersey是否足够智能来处理一个简单的数组:

 @POST @Path("FileCollection") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile(@FormDataParam("test") String[] inputs) { //handle the request } 

但arrays未按预期注入。

MultiValuedMap

我悲惨地失败了,我记得MultiValuedMap对象可以开箱即用。

 @POST @Path("FileCollection") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile(MultiValuedMap formData) { //handle the request } 

但它也不起作用。 这一次,我得到了一个例外

 SEVERE: A message body reader for Java class javax.ws.rs.core.MultivaluedMap, and Java type javax.ws.rs.core.MultivaluedMap, and MIME media type multipart/form-data; boundary=----WebKitFormBoundaryxgxeXiWk62fcLALU was not found. 

我被告知可以通过包含mimepull库来摆脱这个exception,所以我将以下依赖项添加到我的pom中:

   org.jvnet mimepull 1.3  

不幸的是问题仍然存在。 这可能是选择正确的身体阅读器和使用不同参数的问题。 我不知道该怎么做。 我想要同时使用文件和文本输入,以及其他一些输入(主要是Long值和自定义参数类)。

FormDataMultipart

经过一些研究,我找到了FormDataMultiPart类。 我已成功使用它从我的表单中提取字符串值

 @POST @Path("upload2") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadMultipart(FormDataMultiPart multiPart){ List fields = multiPart.getFields("test"); System.out.println("Name\tValue"); for(FormDataBodyPart field : fields){ System.out.println(field.getName() + "\t" + field.getValue()); //handle the values } //prepare the response } 

问题是,这是我的问题的简化版本的解决方案。 虽然我知道Jersey注入的每一个参数都是通过在某个时刻解析一个字符串来创建的(难怪它毕竟是HTTP)并且我有一些编写自己的参数类的经验,但我真的不知道如何将这些字段转换为InputStreamFile实例以供进一步处理。

因此,在深入了解泽西源代码以查看这些对象是如何创建之前,我决定在这里询问是否有更简单的方法来读取一组(未知大小)的文件。 你知道如何解决这个难题吗?

我通过使用FormDataMultipart的示例找到了解决方案。 事实certificate我非常接近答案。

FormDataBodyPart类提供了一种方法,允许其用户将值读取为InputStream (或理论上,任何其他类,其中存在消息体读取器)。

这是最终的解决方案:

形成

表格保持不变。 我有几个同名的字段,我可以在其中放置文件。 可以使用multiple表单输入(在从目录上载许多文件时需要这些输入)和多个共享名称的输入(从不同位置上载未指定数量的文件的灵活方式)。 也可以使用JavaScript附加更多输入的表单。

 
Multiple inputs with the same name

服务 – 使用FormDataMultipart

这是一个从多部分表单中读取文件集合的简化方法。 具有相同的所有输入都分配给List并使用FormDataBodyPartgetValueAs方法将它们的值转换为InputStream 。 一旦将这些文件作为InputStream实例,就可以轻松地对它们进行任何操作。

 @POST @Path("files") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadMultipart(FormDataMultiPart multiPart) throws IOException{ List fields = multiPart.getFields("test"); for(FormDataBodyPart field : fields){ handleInputStream(field.getValueAs(InputStream.class)); } //prepare the response } private void handleInputStream(InputStream is){ //read the stream any way you want } 
 @Path("/upload/multiples") @POST @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) public Response uploadImage(@FormDataParam("image") List imageDatas){ for( FormDataBodyPart imageData : imageDatas ){ // Your actual code. imageData.getValueAs(InputStream.class); } } 

如果有人试图使用与我相同的name属性进行genericstype=text输入框,您将能够将它们切换为type=hidden输入并将其作为@FormParam("inputName") List nameList您路线中的@FormParam("inputName") List nameList

显然,当切换到隐藏输入时,唯一的一点是仍然将数据发送到服务器而不为其创建UI元素,因此您需要切换到备用显示UI(例如,我使用按钮元素轻松点击 – 删除function)。