使用 Jersey 发送多个文件:找不到 multipart/form-data 的 MessageBodyWriter

Sending multiple files with Jersey: MessageBodyWriter not found for multipart/form-data

我是 java 休息应用程序的新手。 我正在尝试 运行 一个应用程序,但我有这个例外

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.access0(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 中,我有一些类似的东西:

cr = service.path(path)
    .type(MediaType.MULTIPART_FORM_DATA)
    .post(ClientResponse.class, listForm);

其中 listFormArrayList<InputStream>

在其余应用程序中我有:

@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 中,我有这些依赖项:

<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 使用 MessageBodyWriters 来处理 Java 对象到请求流(或服务器端的响应流)的序列化,并使用 MessageBodyReaders 来处理 de -将响应流(或服务器端的请求流)序列化为 Java 个对象。您可以在 JAX-RS Entity Providers.

阅读更多内容

话虽这么说,但每当您看到类似 "No MessageBodyReader(Writer) found for type so and so and java type so and so" 的错误时,它意味着没有序列化程序来处理转换。在你的特殊情况下,它是说没有作家可以处理 ArrayList 到 multipart/form-data 的转换。这是有道理的,因为作者将如何知道如何进行这种转换。

对于多部分,您需要在客户端实际使用多部分API。作者知道如何将这些 API 对象转换为所需的多部分。 API的完整列表可以看包com.sun.jersey.multipart .

这里是发送文件的例子

@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();
}

确实没有关于 1.x 对 Jersey 多部分支持的文档。但很多时候,最好的文档是在测试中看到的。所以如果你想要更多的例子,look at the tests for jersey-multipart.


更新

这是一个使用 Jersey 测试框架的完整测试。 运行 它与任何其他 JUnit 测试一样。将常量 FILE_ONEFILE_TWO 替换为系统上某些任意文件的位置。您应该会看到保存到当前工作目录(很可能是项目根目录)的文件

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_ONEFILE_TWO 替换为您系统上的一些文件位置,然后 运行 测试。这些文件应保存到您当前的工作目录。

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);
        }
    }
}