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
若是沒有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完成文件上傳的過程當中,我遇到的一些問題須要你們特別關注:
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