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
相關文章
相關標籤/搜索