SpringBoot整合Jersey2.x实现文件上传API

前言

SpringBoot的官方文档中关于Jersey的介绍并非很全面: 27.3 JAX-RS and Jersey,SpringBoot-Sample项目里面也只有很是基础的代码,对于一些复杂的经常使用需求,这个文档给不了任何帮助。html

为了使用Jersey提供的Restful API完成文件上传功能,今天我花了很多时间查阅文档资料,遇到了一些问题,而后不断地踩坑尝试,其中一些坑仍是参照Stack Overflow的解决方案,甚至是框架官方文档的说明而碰到的。主要问题就是,SpringBoot和Jersey的官方文档没有给出更详细的内容,Stack Overflow针对的问题很片面,不能适用于全部的状况,因此我打算将搭建项目的过程从头至尾写下来,以便有一个方便参照的教程。java

项目搭建

我使用了Spring发布的Spring Tool Suit(STS)来建立项目,由于这个IDE使用SpringBoot十分方便,能够在建立项目时引入一些技术栈。spring

我使用的Java版本是JDK 8,后面会用到CURL这个命令行工具来测试相关的接口,你们能够先准备好,以便学习过程连贯。apache

这个项目命名为demo-app,默认包为org.demo,我在建立时仅添加了Jersey的支持,SpringBoot版本是1.5.10。api

clipboard.png

若是没有STS,也能够用Eclipse建立一个Maven项目,Pom文件配置以下:tomcat

<?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>org.demo</groupId>
    <artifactId>demo-app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jersey</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

项目建立完成后,须要添加一个jersey-media-multipart的依赖,在pom中dependencies标签中添加:restful

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

这个依赖不须要版本号,由于在spring-boot-starter-parent中已经定义了版本,咱们只须要添加一个dependency在具体项目中便可。app

编写代码

项目建立好时,已经存在一个带有main方法的入口类DemoAppApplication,咱们不须要改动它。框架

Jersey的官方文档中将Restful API调用的入口称做Resources,而在SpringBoot的示例代码中将其命名为Endpoint,其实指的是同一个东西。由于使用了SpringBoot,为了风格统一我使用了Endpoint的命名规则,这不是强制的,你们也能够自定义命名规则。但建议从这二者中选择一种,以便你们方便理解。curl

首先增长一个HelloEndpoint类:

package org.demo;

import java.io.IOException;
import java.io.InputStream;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;

import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.springframework.stereotype.Component;

@Component
@Path("/file")
public class FileUploadEndpoint {

    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String upload(@FormDataParam("file") InputStream fis,
            @FormDataParam("file") FormDataContentDisposition fileDisposition) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            IOUtils.copy(fis, baos);
            String content = new String(baos.toByteArray());
            return content;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

它十分相似于SpringMVC的Controller,可是拥有更规范更严格的REST风格,并且它不能像SpringMVC同样经过返回一个视图名称指向某个视图。

其次是JerseyConfig类:

package org.demo;

import javax.ws.rs.ApplicationPath;

import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

@Component
@ApplicationPath("/rest/demo")
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(MultiPartFeature.class);
        register(FileUploadEndpoint.class);
    }

}

至此,一个文件上传的服务端接口已经编写完成。关于表单页面这里很少作说明,由于这个项目不是一个Web项目,而是Web Service的服务端,不能在项目中直接访问静态Web资源。也不建议使用特殊方法知足这种须要,咱们应当保持项目的纯净。

须要注意@ApplicationPath这个注解,它决定了全部Endpoint的基础路径。

运行测试

下面咱们来测试一下这个Restful API是否能正常工做,运行DemoAppApplication,等待项目部署。

接下来准备使用CURL测试,CURL能够到官网下载操做系统对应的版本。建立一个demo.txt文件保存到任意目录,文件内容写入Test my restful api with curl.,使用英文是为了不CMD命令行中的中文出现乱码的状况,这里不用过多在乎,咱们只要关注结果。

进入demo.txt文件所在的目录,按住Shift打开CMD或者PowerShell(Linux系统下打开终端定位到该目录),执行:curl -X POST -F "file=@demo.txt" http://localhost:8080/rest/demo/file

若是获得Test my restful api with curl.的回显,说明Restful API部署成功,而且可以接收上传的文件。

注意问题

在实现利用Jersey完成文件上传的过程当中,我遇到的一些问题须要你们特别关注:

  • SpringBoot没有默认添加jersey-media-multipart依赖,仅预先定义了须要的版本,若是未引入这个包,将没法使用@FormDataParam注解和Multipart相关的类,没法对Multipart内容进行解析;
  • JerseyConfig中须要注册MultiPartFeature.class,不然会出现报错,没法正确注入文件输入流对象。报错的时机根据是否延迟加载决定,若是application.properties定义了spring.jersey.servlet.load-on-startup=1,会在项目启动时报错;不然会在首次上传文件,初始化FileUploadEndpoint时报错。
  • 关于FileUploadEndpoint.upload()方法,有些文档和教程中,fileDisposition对象使用了ContentDisposition类来定义,尽管它是FormDataContentDisposition的父类,但仍然会报错,缘由未知。建议直接使用FormDataContentDisposition
相关文章
相关标签/搜索