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阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容