最近嘗試使用OKHttp替代Volley作為網絡請求框架,這肯定是要對OKHttp進行重新封裝的,所有封裝完畢,就想對網絡請求功能進行測試,基本的GET,POST驗證通過了,但是在測試文件上傳的時候遇到了門檻,需要自己搭建后臺服務器來對此進行支持,這里記錄一下這個過程,一方面對此做一次總結,同時也為后面使用OkHTTP上傳文件監聽進度回調做準備;還有就是希望能夠幫助到同樣有這個需求的朋友們。
一.SpringMVC服務器構建
1.后臺服務器環境搭建
這邊我并不想花太多的篇幅來講解這一個過程(我能說我對后臺也不太了解嘛?這些步驟純碎百度),只是簡單的講一下思路.
首先需要做的就是配置JAVA開發環境,這個做Android開發的肯定提前都配置好了,沒什么好多說的。
其次需要搭建一個tomcat服務器,我們只需要從官網現在一個tomcat壓縮包,解壓,配置一下TOMCAT_HOME等環境變量就行,可參考(http://jingyan.baidu.com/article/8065f87fcc0f182330249841.html),這邊我用的是Tomcat8.0版本。
下載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
-
下載一個Eclipse,對其添加tomcat服務器設置,window->Preferences->Server->Runtime Environment,
按照下圖的步驟,后臺的開發環境基本就配置完成了。
- 啟動tomcat服務,打開瀏覽器,輸入http://localhost:8080如果看到tomcat首頁,說明tomcat服務器配置成功。
2.服務器代碼構建
- 首先我們打開Eclipse,創建一個Dynamic Web Project,
- 這邊我們先看一下最終的項目結構
-
下面看下對應的代碼
FileModel.javapublic 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>
以上代碼配置完成后,將我們創建的工程添加到tomcat容器中,然后啟動tomcat,打開瀏覽器輸入http://localhost:8080/webapp/fileUploadPage,可以看到我們的fileUpload頁面。webapp根據自己創建的Dynamic Web Project時的設置而定,我創建Dynamic Web Project時設置了Context root為webapp,默認為工程名字;fileUploadPage是Controller中RequestMapping注解中設置的內容。頁面如下,至此,服務器端配置完全結束,我們也可以用一下,文件上傳成功會跳轉到success.jsp
二.Android使用okhttp實現文件上傳
android端如何使用okhttp進行網絡請求,這邊不做介紹了,大家可以自定搜索了解一下,相信大多數人都是知道的,畢竟現在okhttp這么火。其實使用okhttp進行文件上傳,也很簡單,跟不同的網絡請求用法沒有多大的區別,無非就是再RequestBody中指定了上傳的文件而已。在查看代碼之前,我想在這邊講一下不同post請求的區別。
-
普通post請求
我們都知道我們是無法在正常情況下看到post請求的請求參數的,post請求協議規定需要將post請求的請求參數放在http請求頭的下面,且兩者之間有一個空行隔開,普通的http post請求,請求參數都為基本數據類型,發送請求時,將這些數據通過key=value鍵值對的方式拼接成字符串,我們可以通過抓包工具看一下
-
單一文件post請求
根據上面的,我們可以發現,如果是上傳單一文件也一樣,只是http請求頭后面的不是鍵值對,而是我們上傳到文件的二進制形式。okhttp中需要使用RequestBody.create(媒體類型,文件)方法,封裝請求body,請求格式如下,數據并沒有截取完全。
-
復合數據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中可以看到服務器輸出
最后我們可以根據日志中顯示的路徑,去指定目錄下查看對應的文件。
這篇文章就寫到這里,最后祝大家國慶快樂!_