java - Jersey 2 多部分/表单数据问题。输入流为空(可用=0)

标签 java spring jersey jackson tomcat8

我面临 Jersey 2 文件上传问题。服务器端的输入流为空。使用 jersey 2.21、jackson 2.5.4、spring 4.1.6.RELEASE(仅适用于 DI)和 spring security 4.0.2.RELEASE 以确保安全。使用JDK 1.8.0_25和Tomcat 8.0.26。

代码:

@POST
@Path("/upload")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public SimpleResult categoryImageUpload(
        @FormDataParam("file") InputStream file,
        @FormDataParam("file") FormDataBodyPart bodyPart) {
        return SimpleResult.success("File Uploaded successfully!!!");
}

文件详细信息来自 FormDataBodyPart,但 InputStream 为空(可用=0)。

Jersey 配置:

@ApplicationPath("api-business")
public class BusinessApplicationConfig extends ResourceConfig {
    public BusinessApplicationConfig() {
        register(RequestContextFilter.class);
        register(MultiPartFeature.class);
        packages("com.smx.biz.api");
    }
}

pom.xml 中的依赖项:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.5.4</version>
    </dependency>

    <!--Jersey-->
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>2.21</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>2.21</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.21</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-multipart</artifactId>
        <version>2.21</version>
    </dependency>

    <!-- Jersey + Spring -->
    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>2.21</version>
    </dependency>

有人可以帮忙解决这个问题吗?我错过了什么吗???

PS:Spring REST 文件上传代码运行良好,InputStream 即将推出。但 Jersey 代码不起作用。使用相同的客户端代码来测试 API。

工作 Spring REST api 代码:

@ResponseStatus(HttpStatus.OK)
@RequestMapping(value = "/business/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
ImageItem categoryPhotoUpload(@RequestBody MultipartFile file) {
    return uploadService.uploadFile(file);
}

我想将 Jersey 用于 api,但不想使用 Spring REST。

有人可以帮忙解决这个问题吗?

最佳答案

我发现当您使用 @FormDataParam("file") InputStream 文件方法时,在幕后参数永远不会被处理,因为 jersey 实际上是在寻找文件。发生的情况(至少从我到目前为止读到的内容来看)是,当请求传入 jersey 时,使用 mimepull 库进行一些 mime 检查,然后将传入文件保存为临时文件。问题是,如果你的参数类型是InputStream,jersey不会处理它,因为没有为InputStream注册ValueFactory。因此,为了使其发挥作用,您必须执行以下操作。

FormDataParamValueFactoryProvider 内部

添加以下实现:

private final class InputStreamFactory extends ValueFactory<InputStream> {

    private final String name;

    public InputStreamFactory(final String name) {
        this.name = name;
    }

    @Override
    public InputStream provide() {
        LOG.info("Processing paramaeter [" + name + "]");
        final FormDataBodyPart part = getEntity().getField(name);
        final BodyPartEntity entity = part != null ? part.getEntityAs(BodyPartEntity.class) : null;

        if (entity != null) {
            try {
                // Create a temporary file.
                final File file = Utils.createTempFile();

                // Move the part (represented either via stream or file) to the specific temporary file.
                entity.moveTo(file);

                //Retreive file via a FileInputStream
                return new FileInputStream(file);
            } catch (final Exception ex) {
                // Unable to create a temporary file or move the file.
                LOG.warn("Error while processing InputStream. " + ex);
            }
        }

        return null;
    }

}

这将允许 jersey 检测 InputStream。

您还必须更改 createValueFactory 方法以反射(reflect)新的 ValueFactoryProvider。

@Override
protected Factory<?> createValueFactory(final Parameter parameter) {
    final Class<?> rawType = parameter.getRawType();
    if (Parameter.Source.ENTITY == parameter.getSource()) {
        if (FormDataMultiPart.class.isAssignableFrom(rawType)) {
            return new FormDataMultiPartFactory();
        } else {
            return null;
        }
    } else if (parameter.getSourceAnnotation().annotationType() == FormDataParam.class) {
        final String paramName = parameter.getSourceName();
        if (paramName == null || paramName.isEmpty()) {
            // Invalid query parameter name
            return null;
        }

        if (Collection.class == rawType || List.class == rawType) {
            final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0);

            if (FormDataBodyPart.class == clazz) {
                // Return a collection of form data body part.
                return new ListFormDataBodyPartValueFactory(paramName);
            } else if (FormDataContentDisposition.class == clazz) {
                // Return a collection of form data content disposition.
                return new ListFormDataContentDispositionFactory(paramName);
            } else {
                // Return a collection of specific type.
                return new FormDataParamValueFactory(parameter, get(parameter));
            }
        } else if (FormDataBodyPart.class == rawType) {
            return new FormDataBodyPartFactory(paramName);
        } else if (FormDataContentDisposition.class == rawType) {
            return new FormDataContentDispositionFactory(paramName);
        } else if (File.class == rawType) {
            return new FileFactory(paramName);
        } else if (InputStream.class == rawType) {
            return new InputStreamFactory(paramName);
        } else {
            return new FormDataParamValueFactory(parameter, get(parameter));
        }
    }

    return null;
}

现在...这就是一个痛苦的地方...如果您不从 github 提取模块源代码并进行更改编译...您必须基本上重新创建以下类才能引用新类通过引用链。

类(class):

  • FormDataParamValueFactoryProvider
  • FormDataParamInjectionFeature(引用:FormDataParamValueFactoryProvider)
  • MultiPartFeature(引用:FormDataParamInjectionFeature)

完成此操作后,您可以使用@FormDataParam("file") InputStream,它将按预期工作。

关于java - Jersey 2 多部分/表单数据问题。输入流为空(可用=0),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32426291/

相关文章:

java - 如何使用 Maven 编写文件?

java - 在提交时将多个值从 spring 表单 jsp 传递到 spring Controller

http - 如何处理 Jersey 发布请求的可变数量参数

java - 从 Jersey ExceptionMapper 生成 JSONP

java - 您可以使用 JAX-RS/Jersey 进行传统的 Servlet 过滤吗?

java - 在 windows 平台上优化 ffmpeg 以使用网络摄像头进行处理

java - Java 8 中的 ArrayList 交叉匹配

java - 过滤掉集合中的记录

java - 如何在 Spring 集成中使用 JAVA 配置创建 http 出站网关?

java - Oauth2 Spring 实现