java 如何从带有 Jersey 的多部分表单中读取多个同名的(文件)输入?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12125277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to read several (file) inputs with the same name from a multipart form with Jersey?
提问by toniedzwiedz
I have successfully developed a service, in which I read files uploaded in a multipart form in Jersey. Here's an extremely simplified version of what I've been doing:
我已经成功开发了一项服务,在该服务中我读取了泽西岛以多部分形式上传的文件。这是我一直在做的事情的一个极其简化的版本:
@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) throws IOException {
//handle the file
}
This works just fine but I've been given a new requirement. In addition to the file I'm uploading, I have to handle an arbitrary number of resources. Let's assume these are image files.
这工作得很好,但我得到了一个新的要求。除了我正在上传的文件之外,我还必须处理任意数量的资源。让我们假设这些是图像文件。
I figured I'd just provide the client with a form with one input for the file, one input for the first image and a button to allow adding more inputs to the form (using AJAX or simply plain JavaScript).
我想我只是为客户端提供一个表单,其中包含一个文件输入、一个输入第一张图像和一个按钮以允许向表单添加更多输入(使用 AJAX 或简单的 JavaScript)。
<form action="blahblahblah" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="file" name="image" />
<input type="button" value="add another image" />
<input type="submit" />
</form>
So the user can append the form with more inputs for images, like this:
因此,用户可以为表单附加更多图像输入,如下所示:
<form action="blahblahblah" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="file" name="image" />
<input type="file" name="image" />
<input type="file" name="image" />
<input type="button" value="add another image" />
<input type="submit" />
</form>
I hoped it would be simple enough to read the fields with the same name as a collection. I've done it successfully with text inputs in MVC .NET and I thought it wouldn't be harder in Jersey. It turns out I was wrong.
我希望读取与集合同名的字段足够简单。我已经使用 MVC .NET 中的文本输入成功地完成了它,我认为在 Jersey 中它不会更难。事实证明我错了。
Having found no tutorials on the subject, I started experimenting.
没有找到有关该主题的教程,我开始尝试。
In order to see how to do it, I dumbed the problem down to simple text inputs.
为了了解如何做到这一点,我将问题简化为简单的文本输入。
<form action="blahblabhblah" method="post" enctype="multipart/form-data">
<fieldset>
<legend>Multiple inputs with the same name</legend>
<input type="text" name="test" />
<input type="text" name="test" />
<input type="text" name="test" />
<input type="text" name="test" />
<input type="submit" value="Upload It" />
</fieldset>
</form>
Obviously, I needed to have some sort of collection as a parameter to my method. Here's what I tried, grouped by collection type.
显然,我需要将某种集合作为我的方法的参数。这是我尝试过的,按集合类型分组。
Array
大批
At first, I checked whether Jersey was smart enough to handle a simple array:
起初,我检查 Jersey 是否足够聪明来处理一个简单的数组:
@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("test") String[] inputs) {
//handle the request
}
but the array wasn't injected as expected.
但阵列没有按预期注入。
MultiValuedMap
多值映射
Having failed miserably, I remembered that MultiValuedMap
objects could be handled out of the box.
惨遭失败后,我记得MultiValuedMap
可以开箱即用地处理对象。
@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(MultiValuedMap<String, String> formData) {
//handle the request
}
but it doesn't work either. This time, I got an exception
但它也不起作用。这一次,我有一个例外
SEVERE: A message body reader for Java class javax.ws.rs.core.MultivaluedMap,
and Java type javax.ws.rs.core.MultivaluedMap<java.lang.String, java.lang.String>,
and MIME media type multipart/form-data;
boundary=----WebKitFormBoundaryxgxeXiWk62fcLALU was not found.
I was told that this exception could be gotten rid of by including the mimepull
library so I added the following dependency to my pom:
有人告诉我,可以通过包含mimepull
库来消除此异常,因此我在 pom 中添加了以下依赖项:
<dependency>
<groupId>org.jvnet</groupId>
<artifactId>mimepull</artifactId>
<version>1.3</version>
</dependency>
Unfortunately the problem persists. It's probably a matter of choosing the right body reader and using different parameters for the generic. I'm not sure how to do this. I want to consume both file and text inputs, as well as some others (mostly Long
values and custom parameter classes).
不幸的是,问题仍然存在。这可能是选择正确的正文阅读器并为泛型使用不同的参数的问题。我不知道该怎么做。我想同时使用文件和文本输入,以及其他一些(主要是Long
值和自定义参数类)。
FormDataMultipart
表单数据多部分
After some more research, I found the FormDataMultiPartclass. I've successfully used it to extract the string values from my form
经过更多研究,我找到了FormDataMultiPart类。我已经成功地使用它从我的表单中提取字符串值
@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMultipart(FormDataMultiPart multiPart){
List<FormDataBodyPart> 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
}
The problem is, this is a solution to the simplified version of my problem. While I know that every single parameter injected by Jersey is created by parsing a string at some point (no wonder, it's HTTP after all) and I have some experience writing my own parameter classes, I don't really how to convert these fields to InputStream
or File
instances for further processing.
问题是,这是我问题的简化版本的解决方案。虽然我知道 Jersey 注入的每个参数都是通过在某个时候解析字符串创建的(难怪,毕竟它是 HTTP)并且我有一些编写自己的参数类的经验,但我真的不知道如何将这些字段转换为InputStream
或File
进一步处理的实例。
Therefore, before diving into Jersey source code to see how these objects are created, I decided to ask here whether there is an easier way to read a set (of unknown size) of files. Do you know how to solve this conundrum?
因此,在深入研究 Jersey 源代码以查看这些对象是如何创建的之前,我决定在这里询问是否有更简单的方法来读取一组(未知大小)文件。你知道如何解决这个难题吗?
回答by toniedzwiedz
I have found the solution by following the example with FormDataMultipart
. It turns out I was very close to the answer.
我通过以下示例找到了解决方案FormDataMultipart
。事实证明,我非常接近答案。
The FormDataBodyPart
class provides a method that allows its user to read the value as InputStream
(or theoretically, any other class, for which a message body reader is present).
的FormDataBodyPart
类提供一种方法,其允许其用户读取的值作为InputStream
(或理论上,任何其他类,针对其消息体读取器存在的话)。
Here's the final solution:
这是最终的解决方案:
Form
形式
The form remains unchanged. I have a couple of fields with the same name, in which I can place files. It's possible to use both multiple
form inputs (you want these when uploading many files from a directory) and numerous inputs that share a name (Flexible way to upload an unspecified number of files from different location). It's also possible to append the form with more inputs using JavaScript.
形式保持不变。我有几个同名的字段,我可以在其中放置文件。可以同时使用multiple
表单输入(从目录上传许多文件时需要这些)和共享名称的大量输入(从不同位置上传未指定数量的文件的灵活方式)。还可以使用 JavaScript 为表单附加更多输入。
<form action="/files" method="post" enctype="multipart/form-data">
<fieldset>
<legend>Multiple inputs with the same name</legend>
<input type="file" name="test" multiple="multiple"/>
<input type="file" name="test" />
<input type="file" name="test" />
</fieldset>
<input type="submit" value="Upload It" />
</form>
Service - using FormDataMultipart
服务 - 使用 FormDataMultipart
Here's a simplified method that reads a collection of files from a multipart form. All inputs with the same are assigned to a List
and their values are converted to InputStream
using the getValueAs
method of FormDataBodyPart
. Once you have these files as InputStream
instances, it's easy to do almost anything with them.
这是一种从多部分表单中读取文件集合的简化方法。具有相同的所有输入都分配给 aList
并InputStream
使用 的getValueAs
方法将它们的值转换为FormDataBodyPart
。一旦您将这些文件作为InputStream
实例,就可以轻松地对它们进行几乎任何操作。
@POST
@Path("files")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMultipart(FormDataMultiPart multiPart) throws IOException{
List<FormDataBodyPart> 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
}
回答by Amit Sharma
@Path("/upload/multiples")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public Response uploadImage(@FormDataParam("image") List<FormDataBodyPart> imageDatas){
for( FormDataBodyPart imageData : imageDatas ){
// Your actual code.
imageData.getValueAs(InputStream.class);
}
}
回答by Blaskovicz
If anyone is trying to do generic type=text
input boxes with the same name
attribute like I was, you will be able to switch them to type=hidden
inputs and have that work as a @FormParam("inputName") List<String> nameList
in your route.
如果有人像我type=text
一样尝试使用具有相同name
属性的通用输入框,您将能够将它们切换到type=hidden
输入并使其@FormParam("inputName") List<String> nameList
在您的路线中工作。
Obviously, when switching to hidden inputs the only point is to still send the data to the server without creating a UI element for it, so you will need to switch to an alternate display UI (for example, I used a button element for easy click-to-remove functionality).
显然,当切换到隐藏输入时,唯一的一点是仍然将数据发送到服务器而不为其创建 UI 元素,因此您将需要切换到备用显示 UI(例如,我使用按钮元素来轻松单击-删除功能)。