【问题标题】:Jersey 2 multipart/form-data issue. InputStream is empty (available=0)Jersey 2 多部分/表单数据问题。 InputStream 为空(可用=0)
【发布时间】:2015-12-02 06:41:32
【问题描述】:

我遇到了 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 为空(available=0)。

球衣配置:

@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 即将到来。但是泽西代码不起作用。使用相同的客户端代码测试 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。

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

【问题讨论】:

    标签: java spring jersey jackson tomcat8


    【解决方案1】:

    我发现当您使用 @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 方法以反映新的 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 拉取模块源并使用更改进行编译...您必须基本上重新创建以下类才能引用新类通过引用链。

    类:

    • FormDataParamValueFactoryProvider
    • FormDataParamInjectionFeature(参考:FormDataParamValueFactoryProvider)
    • MultiPartFeature(参考:FormDataParamInjectionFeature)

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

    【讨论】:

      【解决方案2】:

      在我的情况下,我将 @FormDataParam("file") InputStream file 替换为 @FormDataParam("file") File file 然后它开始正常工作。

      【讨论】:

        【解决方案3】:

        确保注解内的字符串

        @FormDataParam("theSameStringUsedInAnnotation") InputStream file

        与您发布的资源的名称完全匹配。

        就我而言,我使用的是 ExtJs 文件上传,当你在那里定义文件上传时:

        xtype: 'filefield', name: 'theSameStringUsedInAnnotation',

        你必须定义相同的字符串

        【讨论】:

          猜你喜欢
          • 2012-10-11
          • 2012-12-26
          • 1970-01-01
          • 1970-01-01
          • 2019-11-19
          • 1970-01-01
          • 2018-07-02
          • 2020-05-18
          • 1970-01-01
          相关资源
          最近更新 更多