使用 Jersey 发送多个文件:找不到用于 multipart/form-data 的 MessageBodyWriter
我是 Java REST 应用程序的新手.我正在尝试运行一个应用程序,但我遇到了这个异常
I'm new in java rest application. I'm trying to run an application, but i have this exception
message com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class java.util.ArrayList, and MIME media type, multipart/form-data, was not found
exception
com.sun.jersey.api.client.ClientHandlerException: com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class java.util.ArrayList, and MIME media type, multipart/form-data, was not found
com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:155)
com.sun.jersey.api.client.Client.handle(Client.java:652)
com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:570)
org.eu.paas.client.APIClient.doPost(APIClient.java:265)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
在 APIClient.java:265
我有一些类似的东西:
In APIClient.java:265
I have some thing like:
cr = service.path(path)
.type(MediaType.MULTIPART_FORM_DATA)
.post(ClientResponse.class, listForm);
其中 listForm
是一个 ArrayList
在其余的应用程序中我有:
and in the rest application I have:
@POST
@Path("{appId-appId}/action/Multideploy/env/{envId-envId}")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_XML)
Response MultideployApplication(
@PathParam("appId-appId") String appid, @PathParam("envId-envId") String envid,
@FormDataParam("file") List<InputStream> uploadedInputStream);
在我的 pom.xml 中我也有这些依赖项:
Also in my pom.xml I have these dependencies:
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>1.18</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
推荐答案
Jersey 使用 MessageBodyWriter
处理 Java 对象到请求流(或服务器端的响应流)的序列化,以及 MessageBodyReader
s 用于处理 de-将响应流(或服务器端的请求流)序列化为 Java 对象.您可以在 JAX-RS 实体提供者.
Jersey uses MessageBodyWriter
s to handle serialization of Java objects to the request stream (or response stream on server side, and MessageBodyReader
s to handle de-serialization of the response stream (or request stream on server side) into Java objects. You can read more at JAX-RS Entity Providers.
话虽如此,每当你看到类似No MessageBodyReader(Writer) found for type so and so and java type so and"这样的错误时,这意味着没有序列化程序可以处理转换.在您的特定情况下,它是说没有编写器可以处理 ArrayList
到 multipart/form-data 的转换.这是有道理的,因为作者将如何知道如何进行这种转换.
That being said, whenever you see an error like "No MessageBodyReader(Writer) found for type so and so and java type so and so", it means that there is no serializer to handle the conversion. In you particular case, it is saying that there is not writer that can handle the conversion of an ArrayList
to multipart/form-data. This makes sense, as how will the writer know how to make this conversion.
对于多部分,您需要在客户端实际使用多部分 API.作者知道如何将这些 API 对象转换为所需的多部分.有关 API 的完整列表,您可以查看包 com.sun.jersey.multipart
.
For multipart, you need to actually use the multipart APIs on the client client side. The writer knows how to convert these API object into the required multipart parts. For the complete list of APIs, you can look at the package com.sun.jersey.multipart
.
这是一个发送文件的例子
Here is an example of sending a file
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String getTest(@FormDataParam("file") InputStream in,
@FormDataParam("file") FormDataContentDisposition fdc) {
try (FileOutputStream out = new FileOutputStream(fdc.getFileName())) {
ReaderWriter.writeTo(in, out);
} catch (IOException ex) {
ex.printStackTrace(System.out);
return "Bad $#!t happended";
}
return "Upload OK";
}
@Test
public void doit() {
File file = new File("...");
FileDataBodyPart filePart = new FileDataBodyPart("file", file);
MultiPart entity = new FormDataMultiPart()
.bodyPart(filePart);
Client client = Client.create();
WebResource resource = client.resource("http://localhost:9998/service");
ClientResponse response = resource
.type(MediaType.MULTIPART_FORM_DATA_TYPE)
.post(ClientResponse.class, entity);
assertEquals(200, response.getStatus());
assertEquals("Upload OK", response.getEntity(String.class));
response.close();
}
对于 Jersey 多部分的 1.x 支持确实没有文档.但是很多时候,最好的文档是在测试中看到的.因此,如果您想要更多示例,查看 jersey-multipart 的测试.
There's really no documentation for the 1.x support for Jersey multipart. But a lot of times, the best documentation is seen in the tests. So if you want some more examples to go off of, look at the tests for jersey-multipart.
这是一个使用 Jersey 测试框架的完整测试.像任何其他 JUnit 测试一样运行它.将常量 FILE_ONE
和 FILE_TWO
替换为系统上一些任意文件的位置.您应该会看到保存到当前工作目录(很可能是项目根目录)的文件
Here is a complete test using Jersey Test Framework. Run it like any other JUnit test. Replace the constants FILE_ONE
and FILE_TWO
with the location of some arbitrary files on your system. You should see the files saved to your current working directory (most likely the project root)
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.core.DefaultResourceConfig;
import com.sun.jersey.core.header.ContentDisposition;
import com.sun.jersey.core.util.ReaderWriter;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataMultiPart;
import com.sun.jersey.multipart.FormDataParam;
import com.sun.jersey.multipart.MultiPart;
import com.sun.jersey.multipart.file.FileDataBodyPart;
import com.sun.jersey.spi.container.servlet.WebComponent;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.WebAppDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* Run this like any other JUnit test. Dependencies for test are as follows.
*
* <jersey.version>1.19</jersey.version>
*
* <dependency>
* <groupId>com.sun.jersey</groupId>
* <artifactId>jersey-servlet</artifactId>
* <version>${jersey.version}</version>
* </dependency>
* <dependency>
* <groupId>com.sun.jersey</groupId>
* <artifactId>jersey-servlet</artifactId>
* <version>${jersey.version}</version>
* </dependency>
* <dependency>
* <groupId>com.sun.jersey.jersey-test-framework</groupId>
* <artifactId>jersey-test-framework-grizzly2</artifactId>
* <version>1.19</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>com.sun.jersey.contribs</groupId>
* <artifactId>jersey-multipart</artifactId>
* <version>${jersey.version}</version>
* </dependency>
*
*/
public class MultipartTest extends JerseyTest {
@Path("service")
public static class Service {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String getTest(@FormDataParam("file") List<FormDataBodyPart> files) {
for (FormDataBodyPart filePart: files) {
ContentDisposition cd = filePart.getContentDisposition();
try (FileOutputStream out = new FileOutputStream(cd.getFileName())) {
ReaderWriter.writeTo(filePart.getEntityAs(InputStream.class), out);
} catch (IOException ex) {
ex.printStackTrace(System.out);
return "Oops";
}
}
return "Upload OK";
}
}
public static class AppConfig extends DefaultResourceConfig {
public AppConfig() {
super(Service.class);
}
}
@Override
public WebAppDescriptor configure() {
return new WebAppDescriptor.Builder()
.initParam(WebComponent.RESOURCE_CONFIG_CLASS,
AppConfig.class.getName())
.build();
}
private static final String FILE_ONE = "<enter-a-file-path>";
private static final String FILE_TWO = "<enter-a-file-path>";
@Test
public void doit() {
File file1 = new File(FILE_ONE);
File file2 = new File(FILE_TWO);
MultiPart entity = new FormDataMultiPart();
FileDataBodyPart filePart = new FileDataBodyPart("file", file1);
entity.bodyPart(filePart);
filePart = new FileDataBodyPart("file", file2);
entity.bodyPart(filePart);
ClientResponse response = resource().path("service")
.type(MediaType.MULTIPART_FORM_DATA_TYPE)
.post(ClientResponse.class, entity);
assertEquals(200, response.getStatus());
assertEquals("Upload OK", response.getEntity(String.class));
response.close();
}
}
更新 2(泽西 2.x)
这是 Jersey 2.x 的相同测试.只需将常量 FILE_ONE
和 FILE_TWO
替换为系统上的一些文件位置,然后运行测试.文件应保存到您当前的工作目录.
UPDATE 2 (Jersey 2.x)
Here is the same test with Jersey 2.x. Just replace the constants FILE_ONE
and FILE_TWO
with some file locations on your system, and run the test. The files should be saved to your current working directory.
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import org.glassfish.jersey.message.internal.ReaderWriter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
/**
* Run it like any other JUnit test. Required dependencies are as follows:
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <version>2.22.1</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>org.glassfish.jersey.media</groupId>
* <artifactId>jersey-media-multipart</artifactId>
* <version>2.22.1</version>
* <scope>test</scope>
* </dependency>
*
*/
public class Jersey2MultipartTest extends JerseyTest {
@Path("service")
public static class Service {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String getTest(@FormDataParam("file") List<FormDataBodyPart> files) {
for (FormDataBodyPart filePart: files) {
ContentDisposition cd = filePart.getContentDisposition();
try (FileOutputStream out = new FileOutputStream(cd.getFileName())) {
ReaderWriter.writeTo(filePart.getEntityAs(InputStream.class), out);
} catch (IOException ex) {
ex.printStackTrace(System.out);
return "Oops";
}
}
return "Upload OK";
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(Service.class)
.register(MultiPartFeature.class);
}
@Override
public void configureClient(ClientConfig config) {
config.register(MultiPartFeature.class);
}
private static final String FILE_ONE = "<enter-file-location>";
private static final String FILE_TWO = "<enter-file-location>";
@Test
public void doit() {
MultiPart entity = new FormDataMultiPart();
addFiles(entity, FILE_ONE, FILE_TWO);
Response response = target("service").request()
.post(Entity.entity(entity, MediaType.MULTIPART_FORM_DATA));
assertEquals(200, response.getStatus());
assertEquals("Upload OK", response.readEntity(String.class));
response.close();
}
private void addFiles(MultiPart entity, String... files) {
for (String file: files) {
FileDataBodyPart filePart = new FileDataBodyPart("file", new File(file));
entity.bodyPart(filePart);
}
}
}
相关文章