如何从Java JAX-RS服务器获取POST请求的消息体数据。

huangapple go评论77阅读模式
英文:

How to get post body data from java jax-rs server

问题

我按照一个简单的Java服务器设置指南进行了操作。

所以我有类似这样的东西:

MyApp.java

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

@ApplicationPath("/")
public class MyApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> h = new HashSet<>();
        h.add(HelloWorld.class);
        return h;
    }
}

HelloWord.java

import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Path("/helloworld")
public class HelloWorld {
    @Context
    private HttpServletRequest httpRequest;

    @GET
    @Produces("text/plain")
    public String getClichedMessage() {
        return "Hello, World!";
    }

    @POST
    @Consumes()
    @Produces("text/plain")
    public String doThis(String x) {
        System.out.println(x);
        //....
    }
}

传入的请求类型是:

content-type = multipart/form-data; boundary=somerandstuff

看起来像这样:

--somerandstuff
Content-Disposition: form-data; name="somedata1"

data text 1here
--somerandstuff
Content-Disposition: form-data; name="somedata2"

more data text here

--somerandstuff
Content-Disposition: form-data; name="numberoffiles"

3
--somerandstuff
Content-Disposition: form-data; name="file1"; filename="firstfile"
Content-Type: application/octet-stream

{unreadable symbols thing here}

.
.
. 2 more files like above

我该如何实际读取数据?我尝试了许多方法,但无法使其正常工作。我尝试使用httpRequest.getParameterMap()httpRequest.getParameterNames()并打印所有内容,但没有任何打印输出。我甚至似乎无法访问POST主体数据。我唯一可以访问的是使用httpRequest.getHeaderNames()获取的标头。

我想要做的是将每个文件存储在File对象(或类似的对象)中,然后下载它。我应该如何做到这一点?我查看了一些关于此主题的帖子,但是当我尝试实现解决方案时,它们似乎不起作用。

编辑:这是我的pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.serverproject</groupId>
    <artifactId>serverProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>serverProject</name>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <junit.version>5.6.2</junit.version>
    </properties>

    <dependencies>
        <!-- Dependencies... -->
    </dependencies>

    <build>
        <plugins>
            <!-- Plugins... -->
        </plugins>
    </build>
</project>
英文:

I followed a simple java server setup guide.

So i have something like this:

MyApp.java

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

@ApplicationPath(&quot;/&quot;)
public class MyApplication extends Application {
    @Override
    public Set&lt;Class&lt;?&gt;&gt; getClasses() {
        Set&lt;Class&lt;?&gt;&gt; h = new HashSet&lt;&gt;();
        h.add( HelloWorld.class );
        return h;
    }
}

and HelloWord.java

import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Path(&quot;/helloworld&quot;)
public class HelloWorld {
    @Context
    private HttpServletRequest httpRequest;

    @GET
    @Produces(&quot;text/plain&quot;)
    public String getClichedMessage() {
        return &quot;Hello, World!&quot;;
    }

    @POST
    @Consumes()
    @Produces(&quot;text/plain&quot;)
    public String doThis(String x) {
        System.out.println(x);
        //....
    }
}

The incoming request is of type

content-type = multipart/form-data; boundary=somerandstuff

and looks like :

--somerandstuff
Content-Disposition: form-data; name=&quot;somedata1&quot;

data text 1here
--somerandstuff
Content-Disposition: form-data; name=&quot;somedata2&quot;

more data text here

--somerandstuff
Content-Disposition: form-data; name=&quot;numberoffiles&quot;

3
--somerandstuff
Content-Disposition: form-data; name=&quot;file1&quot;; filename=&quot;firstfile&quot;
Content-Type: application/octet-stream

{unreadable symbols thing here}

.
.
. 2 more files like above

How do I go about actually reading the data? I've tried many things and can't get it to work. I tried using httpRequest.getParameterMap() and httpRequest.getParameterNames() and printing everything but nothing prints. I can't seem to even access the post body data. The only things I can access are the headers using httpRequest.getHeaderNames().

What I want to do is store each file in a File object (or something similar) and then download it. How would i go about doing that? I've looked at a few posts on this topic but when I try implementing the solutions, they don't seem to work.

Edit: here is my pom.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

    &lt;groupId&gt;com.serverproject&lt;/groupId&gt;
    &lt;artifactId&gt;serverProject&lt;/artifactId&gt;
    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;serverProject&lt;/name&gt;
    &lt;packaging&gt;war&lt;/packaging&gt;

    &lt;properties&gt;
        &lt;maven.compiler.target&gt;1.8&lt;/maven.compiler.target&gt;
        &lt;maven.compiler.source&gt;1.8&lt;/maven.compiler.source&gt;
        &lt;junit.version&gt;5.6.2&lt;/junit.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax&lt;/groupId&gt;
            &lt;artifactId&gt;javaee-web-api&lt;/artifactId&gt;
            &lt;version&gt;8.0.1&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.glassfish.jersey.containers&lt;/groupId&gt;
            &lt;artifactId&gt;jersey-container-servlet&lt;/artifactId&gt;
            &lt;version&gt;2.31&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.glassfish.jersey.media&lt;/groupId&gt;
            &lt;artifactId&gt;jersey-media-json-jackson&lt;/artifactId&gt;
            &lt;version&gt;2.31&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.glassfish.jersey.inject&lt;/groupId&gt;
            &lt;artifactId&gt;jersey-hk2&lt;/artifactId&gt;
            &lt;version&gt;2.31&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.glassfish.jersey.core&lt;/groupId&gt;
            &lt;artifactId&gt;jersey-client&lt;/artifactId&gt;
            &lt;version&gt;2.31&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.junit.jupiter&lt;/groupId&gt;
            &lt;artifactId&gt;junit-jupiter-api&lt;/artifactId&gt;
            &lt;version&gt;${junit.version}&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.junit.jupiter&lt;/groupId&gt;
            &lt;artifactId&gt;junit-jupiter-engine&lt;/artifactId&gt;
            &lt;version&gt;${junit.version}&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
                &lt;version&gt;3.3.0&lt;/version&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;

答案1

得分: 2

以下是翻译好的内容:

<!-- 添加以下依赖到你的 pom.xml 文件 -->
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>2.31</version>
</dependency>
// 在你的 Application 子类中注册 MultiPartFeature 类
@ApplicationPath("/")
public class MyApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> h = new HashSet<>();
        h.add(HelloWorld.class);
        h.add(MultiPartFeature.class);
        return h;
    }
}
// 在资源方法中使用 @FormDataParam 注解声明每个部分
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response fileUpload(@FormDataParam("somedata1") String someData1,
                           @FormDataParam("numberoffiles") int numberOfFiles,
                           @FormDataParam("file1") InputStream firstFile,
                           @FormDataParam("file1") FormDataContentDisposition fdcd) {
    // 根据期望的数据类型来确定参数类型
    // ...
}
// 如果文件数量未知,可以使用 FormDataMultiPart 类来提取每个部分
public Response fileUpload(FormDataMultiPart multipart) {
    Map<String, List<FormDataBodyPart>> bodyParts = multipart.getFields();
    FormDataBodyPart someDataPart = multipart.getField("somedata1");
    String someData = someDataPart.getValueAs(String.class);
    // 遍历 FormDataBodyPart 并使用 bodyPart.getValueAs(Class) 方法提取数据
    // ...
}

更多信息,请查阅完整文档

英文:

Ok so what you have is multipart data, which is most commonly used to send multiple files and metadata (or other data) along with those files. There is no standard JAX-RS support for multipart, but you are using Jersey (a JAX-RS implementation) and Jersey does have support for multipart. The first thing you need to do is add a new dependency to your pom.xml file

&lt;dependency&gt;
    &lt;groupId&gt;org.glassfish.jersey.media&lt;/groupId&gt;
    &lt;artifactId&gt;jersey-media-multipart&lt;/artifactId&gt;
    &lt;version&gt;2.31&lt;/version&gt;
&lt;/dependency&gt;

The next thing you need to do is register this feature with your application. Since you are using an Application subclass, you would add the MultiPartFeature class to the classes in getClasses()

@ApplicationPath(&quot;/&quot;)
public class MyApplication extends Application {
    @Override
    public Set&lt;Class&lt;?&gt;&gt; getClasses() {
        Set&lt;Class&lt;?&gt;&gt; h = new HashSet&lt;&gt;();
        h.add(HelloWorld.class);
        h.add(MultiPartFeature.class);
        return h;
    }
}

Now we can use this feature. In your resource method, what we will do is declare each part using Jersey's @FormDataParam annotation. As you can see in the multipart entity, each part has a name

Content-Disposition: form-data; name=&quot;somedata1&quot;

Here the name of this part is somedata1. So we will add a parameter for this part. Also notice I will add @Consumes(MediaType.MULTIPART_FORM_DATA) as that is the content-type our method will be consuming.

@POST
@Path(&quot;upload&quot;)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response fileUpload(@FormDataParam(&quot;somedata1&quot;) String someData1) {
    ...
}

Based on what type of data is expected, that's how you will determine the parameter type. The type of somedata1 is just plain text, that's why I used a String. But the numeroffiles part is a number, so I can use an int for that part

public Response fileUpload(@FormDataParam(&quot;somedata1&quot;) String someData1,
                           @FormDataParam(&quot;numberoffiles&quot;) int numberOfFiles) {
    ...
}

The firtfile part is going to be a binary file. So what we can use an InputStream, File, or byte[] parameter. We can also add an additional parameter of type FormDataContentDisposition which will give us some info about this file. For this example, I will use an InputStream. Just google "how to save an InputStream to a file in Java"

public Response fileUpload(@FormDataParam(&quot;somedata1&quot;) String someData1,
                           @FormDataParam(&quot;numberoffiles&quot;) int numberOfFiles,
                           @FormDataParam(&quot;file1&quot;) InputStream firstFile,
                           @FormDataParam(&quot;file1&quot;) FormDataContentDisposition fdcd) {
    ...
    String fileName = fdcd.getFileName();
}

UPDATE

If the number of files is unknown, instead of decalring all these separate parts, what you can do is just declare one parameter of type FormDataMultiPart and programmatically extract each part

public Response fileUpload(FormDataMultiPart multipart) {
    Map&lt;String,List&lt;FormDataBodyPart&gt;&gt; bodyParts = multipart.getFields();
    FormDataBodyPart someDataPart = multipart.getField(&quot;somedata1&quot;);
    String someData = someDataPart.getValueAs(String.class);
}

You will need to traverse all the FormDataBodyParts and you use the bodyPart.getValueAs(Class) method to extract the data of the body part. For the files, you would use getValueAs(InputStream.class).

And that's pretty much it. That's how you work with multipart in Jersey. For more info, you can read the complete docs

huangapple
  • 本文由 发表于 2020年10月11日 04:42:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/64298109.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定