SpringMVC服務+Android端OKHttp文件上傳

最近嘗試使用OKHttp替代Volley作為網絡請求框架,這肯定是要對OKHttp進行重新封裝的,所有封裝完畢,就想對網絡請求功能進行測試,基本的GET,POST驗證通過了,但是在測試文件上傳的時候遇到了門檻,需要自己搭建后臺服務器來對此進行支持,這里記錄一下這個過程,一方面對此做一次總結,同時也為后面使用OkHTTP上傳文件監聽進度回調做準備;還有就是希望能夠幫助到同樣有這個需求的朋友們。

一.SpringMVC服務器構建

1.后臺服務器環境搭建

這邊我并不想花太多的篇幅來講解這一個過程(我能說我對后臺也不太了解嘛?這些步驟純碎百度),只是簡單的講一下思路.

  1. 首先需要做的就是配置JAVA開發環境,這個做Android開發的肯定提前都配置好了,沒什么好多說的。

  2. 其次需要搭建一個tomcat服務器,我們只需要從官網現在一個tomcat壓縮包,解壓,配置一下TOMCAT_HOME等環境變量就行,可參考(http://jingyan.baidu.com/article/8065f87fcc0f182330249841.html),這邊我用的是Tomcat8.0版本。

  3. 下載SpringMVC相關的庫,目前Spring Framework官網都是給出的通過Maven,Gradle等構建工具使用Spring,我們沒必要再去搭Maven,Gradle(當然,你有足夠的興趣跟經歷,也可以嘗試一下,我比較懶,哈哈),我們可以手動下載SpringFramework,我這邊使用的是spring-framework-4.3.6,下載地址:http://repo.spring.io/release/org/springframework/spring/4.3.6.RELEASE/,此處,我一共引入了如下jar

SpringMVC需要的jar.png
  1. 下載一個Eclipse,對其添加tomcat服務器設置,window->Preferences->Server->Runtime Environment,

    按照下圖的步驟,后臺的開發環境基本就配置完成了。

eclipse配置tomcat.png
  1. 啟動tomcat服務,打開瀏覽器,輸入http://localhost:8080如果看到tomcat首頁,說明tomcat服務器配置成功。
2.服務器代碼構建
  1. 首先我們打開Eclipse,創建一個Dynamic Web Project,
創建動態web項目.png
  1. 這邊我們先看一下最終的項目結構
服務器工程目錄結構.png
  1. 下面看下對應的代碼
    FileModel.java

    public class FileModel {
     private MultipartFile file;
    
        public MultipartFile getFile() {
           return file;
        }
    
        public void setFile(MultipartFile file) {
           this.file = file;
        }
    }
    

    FileUploadController.java

    @Controller
    public class FileUploadController {
    
     @Autowired
        ServletContext context; 
    
        @RequestMapping(value = "/fileUploadPage", method = RequestMethod.GET)
        public ModelAndView fileUploadPage() {
           FileModel file = new FileModel();
           ModelAndView modelAndView = new ModelAndView("fileUpload", "command", file);
           return modelAndView;
        }
    
        @RequestMapping(value="/fileUploadPage", method = RequestMethod.POST)
        public String fileUpload(@Validated FileModel file, BindingResult result, ModelMap model) throws IOException {
           if (result.hasErrors()) {
              System.out.println("validation errors");
              return "fileUploadPage";
           } else {            
              System.out.println("Fetching file");
              MultipartFile multipartFile = file.getFile();
              String uploadPath = context.getContextPath() + File.separator + "temp" + File.separator;
              //Now do something with file...
              File saveFile = new File(uploadPath+file.getFile().getOriginalFilename());
              System.out.println("File save path:" + saveFile.getAbsolutePath());
              FileCopyUtils.copy(file.getFile().getBytes(), saveFile);
              String fileName = multipartFile.getOriginalFilename();
              model.addAttribute("fileName", fileName);
              return "success";
           }
        }
    }
    

    fileUpload.js

    <%@ page contentType="text/html; charset=UTF-8"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
    <html>
    <head>
    <title>Spring MVC上傳文件示例</title>
    </head>
    <body>
        <form:form method="POST" modelAttribute="fileUpload"
            enctype="multipart/form-data">
          請選擇一個文件上傳 : 
          <input type="file" name="file" />
            <input type="submit" value="提交上傳" />
        </form:form>
    </body>
    </html>
    

    success.js

    <%@ page contentType="text/html; charset=UTF-8"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
    <html>
    <head>
    <title>Spring MVC上傳文件示例</title>
    </head>
    <body>
        <form:form method="POST" modelAttribute="fileUpload"
            enctype="multipart/form-data">
          請選擇一個文件上傳 : 
          <input type="file" name="file" />
            <input type="submit" value="提交上傳" />
        </form:form>
    </body>
    </html>
    

    spring-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd 
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-3.1.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ">
    
        <!-- 組件掃描:掃描標記@Controller標記的類,注入到spring容器中 -->
        <context:component-scan base-package="net.spring.controller" />
        
        <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
        </bean>
    
        <bean id="multipartResolver"
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
    </beans>
    

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
     id="WebApp_ID" 
     version="3.1">
      <display-name>HelloSpringMVC</display-name>
      
      <servlet>
            <servlet-name>spring</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- load-on-startup:表示啟動容器時初始化該Servlet; -->
            <load-on-startup>1</load-on-startup>
       </servlet>
      
      <servlet-mapping>
            <servlet-name>spring</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
      
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file> 
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    
  2. 以上代碼配置完成后,將我們創建的工程添加到tomcat容器中,然后啟動tomcat,打開瀏覽器輸入http://localhost:8080/webapp/fileUploadPage,可以看到我們的fileUpload頁面。webapp根據自己創建的Dynamic Web Project時的設置而定,我創建Dynamic Web Project時設置了Context root為webapp,默認為工程名字;fileUploadPage是Controller中RequestMapping注解中設置的內容。頁面如下,至此,服務器端配置完全結束,我們也可以用一下,文件上傳成功會跳轉到success.jsp

服務器上傳文件頁面.png

二.Android使用okhttp實現文件上傳

android端如何使用okhttp進行網絡請求,這邊不做介紹了,大家可以自定搜索了解一下,相信大多數人都是知道的,畢竟現在okhttp這么火。其實使用okhttp進行文件上傳,也很簡單,跟不同的網絡請求用法沒有多大的區別,無非就是再RequestBody中指定了上傳的文件而已。在查看代碼之前,我想在這邊講一下不同post請求的區別。

  1. 普通post請求

    我們都知道我們是無法在正常情況下看到post請求的請求參數的,post請求協議規定需要將post請求的請求參數放在http請求頭的下面,且兩者之間有一個空行隔開,普通的http post請求,請求參數都為基本數據類型,發送請求時,將這些數據通過key=value鍵值對的方式拼接成字符串,我們可以通過抓包工具看一下

抓包_post_基本數據.png
  1. 單一文件post請求

    根據上面的,我們可以發現,如果是上傳單一文件也一樣,只是http請求頭后面的不是鍵值對,而是我們上傳到文件的二進制形式。okhttp中需要使用RequestBody.create(媒體類型,文件)方法,封裝請求body,請求格式如下,數據并沒有截取完全。

抓包_post_單一文件.png
  1. 復合數據post請求

    這邊我們需要考慮還有一種情況,復合數據的post請求,即post請求既有文件,又有鍵值對,甚至還有其他數據類型的時候,這個時候的post請求body就跟前兩者有所區別了,如下,可以發現,它會將每一個請求數據進行分離,并記錄每一個字段的信息,如key,value,內容長度,數據流類型等

    --fb12146e-52e3-4b7b-9345-6768dbcad759
    Content-Disposition: form-data; name="file"; filename="xxx.jar"
    Content-Type: application/octet-stream
    Content-Length: 208700
    
    [[二進制文件數據]]
    --fb12146e-52e3-4b7b-9345-6768dbcad759
    Content-Disposition: form-data; name="username"
    Content-Length: 13
    
    name_for_test
    --fb12146e-52e3-4b7b-9345-6768dbcad759
    Content-Disposition: form-data; name="password"
    Content-Length: 12
    
    pwd_for_test
    --fb12146e-52e3-4b7b-9345-6768dbcad759--
    

    OKHttp中將這種情況封裝到了MultipartBody中,看了上面的格式,就不難理解MultipartBody中的addPart方法了

    new MultipartBody.Builder()
      .addPart(Headers.of(
                "Content-Disposition",
                "form-data; name=\"file\"; filename=\"xxx.jar\"")
                    , fileBody);
    //等價于下面這行代碼
    new MultipartBody.Builder()
      .addFormDataPart("params","xxx.jar",fileBody);
    

講了上面的知識,下面看一下Andorid端的上傳代碼,其實很簡單,file為我們需要上傳的文件,這邊不要忘記啟動tomcat,同時修改url的值。

RequestBody filebody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
        RequestBody body = new MultipartBody.Builder()
                .addFormDataPart("file", file.getName(), filebody)
                .build();
        Request request = new Request.Builder()
                .url("http://192.168.1.106:8080/webapp/fileUploadPage")
                .post(body)
                .build();

        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "請求失敗:" + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e(TAG, "請求成功!");
            }
        });

發送請求后,我們可以在Logcat中看到“請求成功!”的字樣,說明請求成了,同時在Eclipse的console中可以看到服務器輸出

請求成功.png

最后我們可以根據日志中顯示的路徑,去指定目錄下查看對應的文件。

這篇文章就寫到這里,最后祝大家國慶快樂!_

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容