文件上传以及Jersey restful Web服务中的其他对象

时间:2014-12-22 19:59:14

标签: java jersey jax-rs multipartform-data postman

我想通过上传图片和员工数据来在系统中创建员工信息。我能够使用球衣进行不同的休息呼叫。但我希望在一次休息电话中实现。 我提供下面的结构。请帮我在这方面做些什么。

@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
        @FormDataParam("file") InputStream fileInputStream,
        @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
        Employee emp) {

//..... business login

}

每当我想要做的时候,我都会在Chrome邮递员中出错。我的Employee json的简单结构如下所示。

{
    "Name": "John",
    "Age": 23,
    "Email": "john@gmail.com",
    "Adrs": {
        "DoorNo": "12-A",
        "Street": "Street-11",
        "City": "Bangalore",
        "Country": "Karnataka"
    }
}

但是我可以通过进行两次不同的调用来实现,但我希望在一次休息调用中实现,以便我可以接收文件以及员工的实际数据。

请求您在这方面提供帮助。

7 个答案:

答案 0 :(得分:83)

你不能有两个Content-Type(从技术上来说,这就是我们在下面所做的,但是它们与多部分的每个部分分开,但主要类型是多部分)。这基本上是你对你的方法所期望的。您期望将mutlipart json一起作为主要媒体类型。 Employee数据需要是多部分的一部分。因此,您可以为@FormDataParam("emp")添加Employee

@FormDataParam("emp") Employee emp) { ...

这是我用于测试的课程

@Path("/multipart")
public class MultipartResource {

    @POST
    @Path("/upload2")
    @Consumes({MediaType.MULTIPART_FORM_DATA})
    public Response uploadFileWithData(
            @FormDataParam("file") InputStream fileInputStream,
            @FormDataParam("file") FormDataContentDisposition cdh,
            @FormDataParam("emp") Employee emp) throws Exception{

        Image img = ImageIO.read(fileInputStream);
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
        System.out.println(cdh.getName());
        System.out.println(emp);

        return Response.ok("Cool Tools!").build();
    } 
}

首先,我刚刚使用客户端API进行了测试,以确保其正常工作

@Test
public void testGetIt() throws Exception {

    final Client client = ClientBuilder.newBuilder()
        .register(MultiPartFeature.class)
        .build();
    WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");

    FileDataBodyPart filePart = new FileDataBodyPart("file", 
                                             new File("stackoverflow.png"));
    // UPDATE: just tested again, and the below code is not needed.
    // It's redundant. Using the FileDataBodyPart already sets the
    // Content-Disposition information
    filePart.setContentDisposition(
            FormDataContentDisposition.name("file")
                                    .fileName("stackoverflow.png").build());

    String empPartJson
            = "{"
            + "  \"id\": 1234,"
            + "  \"name\": \"Peeskillet\""
            + "}";

    MultiPart multipartEntity = new FormDataMultiPart()
            .field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
            .bodyPart(filePart);

    Response response = t.request().post(
            Entity.entity(multipartEntity, multipartEntity.getMediaType()));
    System.out.println(response.getStatus());
    System.out.println(response.readEntity(String.class));

    response.close();
}

我刚创建了一个简单的Employee类,其中包含idname字段供测试。这完全没问题。它显示图像,打印内容配置,并打印Employee对象。

我对Postman不太熟悉,所以我保存了最后的测试: - )

enter image description here

它似乎也可以正常工作,因为您可以看到响应"Cool Tools"。但是,如果我们查看打印的Employee数据,我们会看到它是空的。这很奇怪,因为客户端API工作正常。

如果我们查看预览窗口,我们会看到问题

enter image description here

Content-Type正文部分没有emp标头。您可以在客户端API中看到我明确设置它

MultiPart multipartEntity = new FormDataMultiPart()
        .field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
        .bodyPart(filePart);

所以我猜这只是部分的完整答案。就像我说的那样,我对Postman并不熟悉因此我不知道如何为个体部位设置Content-Type s。我为图像部分自动设置了图像的image/png(我猜它只是由文件扩展名决定)。如果你能解决这个问题,那么问题就应该解决了。如果您发现如何操作,请将其作为答案发布。


只是为了完整......

基本配置:

相关性:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>${jersey2.version}</version>
</dependency>

客户端配置:

final Client client = ClientBuilder.newBuilder()
    .register(MultiPartFeature.class)
    .build();

服务器配置:

// Create JAX-RS application.
final Application application = new ResourceConfig()
    .packages("org.glassfish.jersey.examples.multipart")
    .register(MultiPartFeature.class);

更新

从Postman客户端可以看出,使用FormData(js)时,一些客户端无法设置单个部件的Content-Type,包括浏览器的默认功能。

我们不能指望客户找到这个,所以我们可以做的是,在接收数据时,在反序列化之前显式设置Content-Type。例如

@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("emp") FormDataBodyPart jsonPart,
                                  @FormDataParam("file") FormDataBodyPart bodyPart) { 
     jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
     Employee emp = jsonPart.getValueAs(Employee.class);
}

获得POJO需要额外的工作,但这比强迫客户尝试找到自己的解决方案更好。


旁白

  • 如果您使用的是与默认HttpUrlConnection不同的连接器,则these comments中有一个您可能感兴趣的对话。

答案 1 :(得分:2)

  

您可以使用MULTIPART FORM DATA从表格访问图像文件和数据使用以下代码。

@POST
@Path("/UpdateProfile")
@Consumes(value={MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
@Produces(value={MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateProfile(
    @FormDataParam("file") InputStream fileInputStream,
    @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
    @FormDataParam("ProfileInfo") String ProfileInfo,
    @FormDataParam("registrationId") String registrationId) {

    String filePath= "/filepath/"+contentDispositionHeader.getFileName();

    OutputStream outputStream = null;
    try {
        int read = 0;
        byte[] bytes = new byte[1024];
        outputStream = new FileOutputStream(new File(filePath));

        while ((read = fileInputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, read);
        }

        outputStream.flush();
        outputStream.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (outputStream != null) { 
            try {
                outputStream.close();
            } catch(Exception ex) {}
        }
    }
}

答案 2 :(得分:0)

您的ApplicationConfig应该注册glassfish.jersey.media中的MultiPartFeature.class ..以便启用文件上传

@javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {  
public ApplicationConfig() {
        //register the necessary headers files needed from client
        register(CORSConfigurationFilter.class);
        //The jackson feature and provider is used for object serialization
        //between client and server objects in to a json
        register(JacksonFeature.class);
        register(JacksonProvider.class);
        //Glassfish multipart file uploader feature
        register(MultiPartFeature.class);
        //inject and registered all resources class using the package
        //not to be tempered with
        packages("com.flexisaf.safhrms.client.resources");
        register(RESTRequestFilter.class);
    }

答案 3 :(得分:0)

我想在peeskillet上添加评论,但不要有50个声望点,因此添加一个答案:

当我使用Jersey客户端2.21.1尝试@peeskillet解决方案时,出现了400错误。当我在我的客户端代码中添加以下内容时,它工作正常:

  MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
  contentType = Boundary.addBoundary(contentType);

  Response response = t.request().post(
        Entity.entity(multipartEntity, contentType));

而不是在请求后调用中硬编码的MediaType.MULTIPART_FORM_DATA。

答案 4 :(得分:0)

请求类型是multipart / form-data,您要发送的本质上是形式为字段的字节形式的字段,内容边界将不同的形式字段分隔开来。来自客户端的序列化表格,然后可以在服务器上反序列化。

毕竟,实际上没有编程环境对象在线路上运行。双方的编程环境都在执行自动序列化和反序列化,您也可以这样做。这是最干净且编程环境怪异的免费方法。

作为示例,这是一个发布到Jersey示例服务的javascript客户端,

submitFile(){

    let data = new FormData();
    let account = {
        "name": "test account",
        "location": "Bangalore"
    }

    data.append('file', this.file);
    data.append("accountKey", "44c85e59-afed-4fb2-884d-b3d85b051c44");
    data.append("device", "test001");
    data.append("account", JSON.stringify(account));
    
    let url = "http://localhost:9090/sensordb/test/file/multipart/upload";

    let config = {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    }

    axios.post(url, data, config).then(function(data){
        console.log('SUCCESS!!');
        console.log(data.data);
    }).catch(function(){
        console.log('FAILURE!!');
    });
},

客户端在这里发送一个文件,2个表单字段(字符串)和一个已经过字符串传输的帐户对象。这是表单域在网上的外观,

enter image description here

在服务器上,您可以按照自己认为合适的方式反序列化表单字段。为了完成这个简单的例子,

    @POST
@Path("/file/multipart/upload")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadMultiPart(@Context ContainerRequestContext requestContext,
        @FormDataParam("file") InputStream fileInputStream,
        @FormDataParam("file") FormDataContentDisposition cdh,
        @FormDataParam("accountKey") String accountKey,
        @FormDataParam("account") String json)  {
    

    
    System.out.println(cdh.getFileName());
    System.out.println(cdh.getName());
    System.out.println(accountKey);
    
    try {
        Account account = Account.deserialize(json);
        System.out.println(account.getLocation());
        System.out.println(account.getName());
        
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    return Response.ok().build();
    
}

答案 5 :(得分:-2)

我使用了文件上传示例,

http://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/

在我的资源类中我有以下方法

@POST
    @Path("/upload")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response  attachupload(@FormDataParam("file") byte[] is,
@FormDataParam("file") FormDataContentDisposition fileDetail,
@FormDataParam("fileName") String flename){
attachService.saveAttachment(flename,is);
}

在我的attachService.java中我有以下方法

 public void saveAttachment(String flename,  byte[] is) {
            // TODO Auto-generated method stub
         attachmentDao.saveAttachment(flename,is);

        }
在道我有

attach.setData(is);
attach.setFileName(flename);
我的HBM映射中的

就像

<property name="data" type="binary" >
            <column name="data" />
</property>

适用于.PDF,.TXT,.PNG等所有类型的文件,

答案 6 :(得分:-4)

在客户端设置“Content-Type:multipart / form-data”,这应该可以完成工作