Java 文件与 Jersey 宁静网络服务中的其他对象一起上传

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27609569/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 04:42:04  来源:igfitidea点击:

File upload along with other object in Jersey restful web service

javajerseyjax-rsmultipartform-datapostman

提问by Sambit

I want to create an employee information in the system by uploading an image along with employee data. I am able to do it with different rest calls using jersey. But I want to achieve in one rest call. I provide below the structure. Please help me how to do in this regard.

我想通过上传图像和员工数据在系统中创建员工信息。我可以使用球衣通过不同的休息电话来做到这一点。但我想在一次休息电话中实现。我提供下面的结构。请帮我这方面怎么做。

@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

}

Whenever I am trying to do, I get error in Chrome postman. The simple structure of my Employee json is given below.

每当我尝试这样做时,Chrome 邮递员都会出错。下面给出了我的 Employee json 的简单结构。

{
    "Name": "John",
    "Age": 23,
    "Email": "[email protected]",
    "Adrs": {
        "DoorNo": "12-A",
        "Street": "Street-11",
        "City": "Bangalore",
        "Country": "Karnataka"
    }
}

However I can do it by making two different call, but I want to achieve in one rest call so that I can receive the file as well as the actual data of the employee.

但是,我可以通过拨打两个不同的电话来实现,但我想在一个休息电话中实现,以便我可以接收文件以及员工的实际数据。

Request you to help in this regard.

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

回答by Paul Samsotha

You can't have two Content-Types (well technically that's what we're doing below, but they are separated with each part of the multipart, but the main type is multipart). That's basically what you are expecting with your method. You are expecting mutlipart andjson together as the main media type. The Employeedata needs to be part of the multipart. So you can add a @FormDataParam("emp")for the Employee.

你不能有两个Content-Types(技术上这就是我们在下面所做的,但它们与 multipart 的每个部分分开,但主要类型是 multipart)。这基本上就是您对方法的期望。您期望 mutlipartjson 一起作为主要媒体类型。该Employee数据需要多部分的一部分。所以你可以@FormDataParam("emp")Employee.

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

Here's the class I used for testing

这是我用于测试的类

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

First I just tested with the client API to make sure it works

首先,我只是用客户端 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();
}

I just created a simple Employeeclass with an idand namefield for testing. This works perfectly fine. It shows the image, prints the content disposition, and prints the Employeeobject.

我刚刚创建了一个Employee带有idandname字段的简单类用于测试。这工作得很好。它显示图像,打印内容配置,并打印Employee对象。

I'm not too familiar with Postman, so I saved that testing for last :-)

我对 Postman 不太熟悉,所以我把那个测试留到最后 :-)

enter image description here

在此处输入图片说明

It appears to work fine also, as you can see the response "Cool Tools". But if we look at the printed Employeedata, we'll see that it's null. Which is weird because with the client API it worked fine.

正如您所看到的响应,它似乎也能正常工作"Cool Tools"。但是如果我们查看打印的Employee数据,我们会发现它是空的。这很奇怪,因为使用客户端 API 时它运行良好。

If we look at the Preview window, we'll see the problem

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

enter image description here

在此处输入图片说明

There's no Content-Typeheader for the empbody part. You can see in the client API I explicitly set it

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

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

So I guess this is really only partof a full answer. Like I said, I am not familiar with Postman So I don't know how to set Content-Types for individual body parts. The image/pngfor the image was automatically set for me for the image part (I guess it was just determined by the file extension). If you can figure this out, then the problem should be solved. Please, if you find out how to do this, post it as an answer.

所以我想这实际上只是完整答案的一部分。就像我说的,我不熟悉 Postman 所以我不知道如何Content-Type为各个身体部位设置s。在image/png对图像进行自动为我设置的图像部分(我猜它只是由文件扩展名确定)。如果你能弄清楚这一点,那么问题就应该解决了。请,如果您知道如何执行此操作,请将其发布为答案。



And just for completeness...

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

Basic configurations:

基本配置:

Dependency:

依赖:

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

Client config:

客户端配置:

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

Server config:

服务器配置:

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


UPDATE

更新

So as you can see from the Postman client, some clients are unable to set individual parts' Content-Type, this includes the browser, in regards to it's default capabilities when using FormData(js).

所以正如你从 Postman 客户端看到的,一些客户端无法设置单个部分的 Content-Type,这包括浏览器,关于它在使用FormData(js)时的默认功能。

We can't expect the client to find away around this, so what we can do, is when receiving the data, explicitly set the Content-Type before deserializing. For example

我们不能指望客户端解决这个问题,所以我们可以做的是,在接收数据时,在反序列化之前显式设置 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);
}

It's a little extra work to get the POJO, but it is a better solution than forcing the client to try and find it's own solution.

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



Asides

旁白

  • There is a conversation in these commentsthat you may be interested in if you are using a different Connector than the default HttpUrlConnection.
  • 如果您使用的连接器与默认的 HttpUrlConnection 不同,那么您可能会对这些评论中的某个对话感兴趣。

回答by zameer

You can access the Image File and data from a form using MULTIPART FORM DATA By using the below code.

您可以使用 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) {}
        }
    }
}

回答by pitaside

Your ApplicationConfig should register the MultiPartFeature.class from the glassfish.jersey.media.. so as to enable file upload

您的 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 Hymanson feature and provider is used for object serialization
        //between client and server objects in to a json
        register(HymansonFeature.class);
        register(HymansonProvider.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);
    }

回答by Raman B

I used file upload example from,

我使用了文件上传示例,

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

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

in my resource class i have below method

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

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

in my attachService.java i have below method

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

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

        }

in Dao i have

在道我有

attach.setData(is);
attach.setFileName(flename);

in my HBM mapping is like

在我的 HBM 映射中

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

This working for all type of files like .PDF,.TXT, .PNG etc.,

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

回答by mkag

I want add a comment on peeskillet but don't have 50 reputation points, hence adding as an answer:

我想在 peeskillet 上添加评论,但没有 50 声望点,因此添加为答案:

When I tried @peeskillet solution with Jersey client 2.21.1, there was 400 error. It worked when I added following in my client code:

当我使用 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));

instead of hardcoded MediaType.MULTIPART_FORM_DATA in post request call.

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