最近尝试使用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中可以看到服务器输出
最后我们可以根据日志中显示的路径,去指定目录下查看对应的文件。
这篇文章就写到这里,最后祝大家国庆快乐!_