云笔记项目- 文件的上传与下载学习

在云笔记项目学习中,提到了如何下载和上传文件,使用web的方式完成上传和下载都需要参考规范协议,代码的书写,以及web的配置,都是参考规范协议来编写的。本文将重新建立一个新Maven项目,进行简单演示,在服务端生成图片和Excel文件,发送到网页显示或实现下载,从本地上传图片或者文件到服务器,然后写出到目标文件夹。

文件上传与下载相关参考协议

(1)文件上传参考协议:Form-based File Upload in HTML RFC1867

(2)文件下载参考协议:Hypertext Transfer Protocol HTTP/1.1 RFC2616

(3)Spring-framework参考文档:https://docs.spring.io/spring/docs/4.3.23.RELEASE/spring-framework-reference/htmlsingle/

  其他协议:

(1)UDP协议:User Datagram Protocol(UDP) RFC768 ,以前学习的UDP编程基于此

(2)TCP协议:Transmission Control Protocol(TCP) RFC793,以前学习的socket就是基于此文章

(3)HTTP/1.0版本协议:Hypertext Transfer Protocol HTTP/1.0 RFC1945,是HTTP/1.1以前的版本

(4)HTTP状态管理协议:HTTP State Management Mechanism RFC6265,其中Cookie就在里面说明

本项目参考的协议只有上传和下载协议,以及Spring-framework参考文档。

项目搭建

Step1 导包,项目需要导入Spring-MVC包、poi包、Commons-fileupload包、jackson核心包、pom.xml中进行配置

 1 <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">
 2   <modelVersion>4.0.0</modelVersion>
 3   <groupId>com.boe</groupId>
 4   <artifactId>FileUploadAndDownLoad</artifactId>
 5   <version>0.0.1-SNAPSHOT</version>
 6   <packaging>war</packaging>
 7   
 8   <dependencies>
 9    <!-- 导入spring-MVC -->
10     <dependency>
11      <groupId>org.springframework</groupId>
12      <artifactId>spring-webmvc</artifactId>
13      <version>4.2.3.RELEASE</version>
14     </dependency>
15   
16     <dependency>
17      <groupId>org.springframework</groupId>
18      <artifactId>spring-webmvc</artifactId>
19      <version>4.2.3.RELEASE</version>
20      <classifier>sources</classifier>
21     </dependency>
22   
23     <dependency>
24      <groupId>org.springframework</groupId>
25      <artifactId>spring-webmvc</artifactId>
26      <version>4.2.3.RELEASE</version>
27      <classifier>javadoc</classifier>
28     </dependency>    
29     
30     <!-- 导入poi包,创建excel用 -->
31     <dependency>
32      <groupId>org.apache.poi</groupId>
33      <artifactId>poi</artifactId>
34      <version>3.17</version>
35     </dependency>
36     
37     <dependency>
38      <groupId>org.apache.poi</groupId>
39      <artifactId>poi-ooxml</artifactId>
40      <version>3.17</version>
41     </dependency>
42     
43     <!-- 导入commons-fileupload包,用于文件上载 -->
44     <dependency>
45      <groupId>commons-fileupload</groupId>
46      <artifactId>commons-fileupload</artifactId>
47      <version>1.3</version>
48     </dependency>   
49     
50     <!-- 导入json和java对象转换的jackson包 -->
51     <!-- 导入jackson-core -->
52     <dependency>
53      <groupId>com.fasterxml.jackson.core</groupId>
54      <artifactId>jackson-core</artifactId>
55      <version>2.2.3</version>
56     </dependency>
57     
58    <!-- 导入jackson-databind -->
59     <dependency>
60      <groupId>com.fasterxml.jackson.core</groupId>
61      <artifactId>jackson-databind</artifactId>
62      <version>2.2.3</version>
63     </dependency>
64     
65     <!-- 导入jackson-annotations -->
66      <dependency>
67       <groupId>com.fasterxml.jackson.core</groupId>
68       <artifactId>jackson-annotations</artifactId>
69       <version>2.2.3</version>
70     </dependency>      
71     
72 </dependencies>
73   
74 </project>

step2 配置Spring-mvc.xml,除了配置组件扫描和注解驱动,由于项目上传文件需要用到Spring的解析器MultipartResolver,因此还需要进行上传解析器配置,里面设置上传的最大文件大小,以及文件名编码方式和最大文件内存保存大小等属性,后面将有用。

 1         <!-- 配置组件扫描 -->
 2         <context:component-scan base-package="Web"></context:component-scan>
 3         <!-- 添加注解驱动 -->
 4         <mvc:annotation-driven></mvc:annotation-driven>
 5         
 6         <!-- 配置文件上载解析器MultipartResolver -->
 7         <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
 8         <!-- one of the properties available; the maximum file size in bytes -->
 9         <!-- 通过set方法设置以下两个父类属性,父类为CommonsFileUploadSupport.class -->
10         <property name="maxUploadSize" value="10000000"/>  <!--10M大小 -->
11         <property name="defaultEncoding" value="UTF-8" /> <!-- 文件名编码,可以适用于中文名 -->
12 
13         <property name="maxInMemorySize" value="10000000" />
14         <!-- 上传文件大小默认小于10kib时,会将文件写入硬盘前保存在内存中,否则就不会保存在内存中 -->
15         </bean>

step3 配置Servlet,其中使用部署描述文件来自动配置,DispatcherServelet会自动启动Spring-MVC,因此不需要新建一个类,直接使用存在的Spring下的DispatcherServelet即可,并设置参数,和路径请求条件。

配置完成后的web.xml文件内容,就包含部署描述文件自动生成的部分。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
 3   <display-name>FileUploadAndDownLoad</display-name>
 4   <welcome-file-list>
 5     <welcome-file>index.html</welcome-file>
 6     <welcome-file>index.htm</welcome-file>
 7     <welcome-file>index.jsp</welcome-file>
 8     <welcome-file>default.html</welcome-file>
 9     <welcome-file>default.htm</welcome-file>
10     <welcome-file>default.jsp</welcome-file>
11   </welcome-file-list>
12   <!-- 手动配置的servlet -->
13   <servlet>
14     <description></description>
15     <display-name>ImageServlet</display-name>
16     <servlet-name>ImageServlet</servlet-name>
17     <servlet-class>Web.ImageServlet</servlet-class>
18   </servlet>
19   <servlet-mapping>
20     <servlet-name>ImageServlet</servlet-name>
21     <url-pattern>/demoImg</url-pattern>
22   </servlet-mapping>
23   <!-- 使用部署描述文件生成,不用新建一个专门的servlet,而是勾选"选择已经存在的servlet或JSP来配置" -->
24   <!-- 使用DispatcherServlet来帮忙加载配置文件,启动Spring容器 -->
25   <servlet>
26     <description></description>
27     <display-name>DispatcherServlet</display-name>
28     <servlet-name>DispatcherServlet</servlet-name>
29     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
30     <init-param>
31       <description></description>
32       <param-name>contextConfigLocation</param-name>
33       <param-value>classpath:config/spring-*.xml</param-value>
34     </init-param>
35     <load-on-startup>1</load-on-startup>
36   </servlet>
37   <servlet-mapping>
38     <servlet-name>DispatcherServlet</servlet-name>
39     <url-pattern>*.do</url-pattern>
40   </servlet-mapping>
41 </web-app>

完成以上配置后,就可以写控制器,以及网页来进行测试,最后测试完成后项目结构如下图:

RFC2616协议参考

服务器给浏览器发送数据时,先发送状态行,再发送响应头和消息body,可以参考RFC2616超文本传输协议内容。

例子:如果发送一个网页给Client,简单的将包含如下几部分
HTTP/1.1 200 OK
ContentType:text/html charset=utf-8
ContentLength:203
空一行
<html>内容</html>

状态行(Status-Line)
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
状态行包括HTTP版本号,状态码,和状态码的建议描述等信息,其中状态码是三位的数字,第一位数字用于分类,其他没有分类功能。此外Client端只检查状态码,不会检查状态码的描述,描述主要是给人看的。关于状态码,有如下几类:
1XX: Informational,请求接受继续执行
2XX: Success,请求被成功接受,解释,以及被接收
3XX: Redirection,为了完成当前请求,需要进行进一步的请求
4XX: Client Error,请求包含错误语法或者不能被实现
5XX: Server Error,服务端不能处理包含正确语法的请求

响应头(Entity Header Fields)
响应头里的ContentType定义了消息Body里的数据类型,根据RFC2616介绍,Content-Encoding也可以定义消息Body数据类型,消息头里的ContentLength定义了消息Body在编码前的长度

消息body(Entity Header Fields)
任何HTTP/1.1版本的消息body,都必须在前面有ContentType对媒体信息的定义。

使用Servlet发送图片到网页

在进行文件下载前,先演示如何动态生成图片,并通过Servlet发送图片到网页显示,其中图片生成有一套固定的写法,最后返回一个byte数组,然后通过Servlet将这个字节数组发送到网页,完成图片显示。在服务器发送图片数据给浏览器时,需要发送包含状态行,响应头和消息Body的一条消息给浏览器,并且要发送正确的消息,网页才能正常显示图片。其中需要设置ContentType为image/png,告诉浏览器这是一张图片,并设置ContentLength,告诉浏览器图片的字节长度是多少。Servlet代码如下:

 1 package Web;
 2 
 3 import java.awt.image.BufferedImage;
 4 import java.io.ByteArrayOutputStream;
 5 import java.io.IOException;
 6 
 7 import javax.imageio.ImageIO;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.http.HttpServlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 
13 public class ImageServlet extends HttpServlet {
14     private static final long serialVersionUID = 1L;
15 
16     public ImageServlet() {
17         // TODO Auto-generated constructor stub
18     }
19 
20     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
21         //response.getWriter().append("Served at: ").append(request.getContextPath());
22         
23         //在doGet方法里将资源发送
24         byte[] image=createImage();
25         //设置消息发送的消息头
26         response.setContentType("image/png");
27         response.setContentLength(image.length);
28         //发送数据
29         response.getOutputStream().write(image);
30         
31     }
32     /**
33      * 绘制一张图片,返回图片编码后的byte数组
34      * @return
35      * @throws IOException 
36      */
37     public byte[] createImage() throws IOException {
38         //使用BufferedImage绘图
39         //第一个参数图片宽度
40         //第二个参数图片高度
41         //第三个参数图片像素颜色表示方式,为3位的byte,一个byte调节一种颜色,分别为BGR,一个byte为8位,可以代表0-255的亮度
42         BufferedImage buffImage=new BufferedImage(100,100,BufferedImage.TYPE_3BYTE_BGR);
43         //设置图片某个像素点的颜色
44         buffImage.setRGB(50, 50, 0x00ff00);//使用16进制表示
45         ByteArrayOutputStream os=new ByteArrayOutputStream();
46         //图片编码为png
47         ImageIO.write(buffImage, "png", os);
48         //关闭流
49         os.close();
50         byte[] image=os.toByteArray();
51         return image;
52     }
53 
54 }

直接使用doGet请求来发送响应,服务端在获取请求后,刚开始会先调用service方法,随后会分发给doGet来执行,具体里面原理这里不深究,后续单独学习。浏览器端访问后图片显示结果如下图:

使用Spring-MVC发送图片内容到网页,并实现文件下载

使用Spring-MVC来实现Servlet能实现的功能,代码将更加简单,如发送图片到网页显示,只需要配置控制器即可,并且使用@ResponseBody注解,可实现向浏览器发送数据,这次发送的数据不一定是JSON,可能是图片字节数组,也可能是Excel的字节数组。如下代码实现了图片发送和文件下载。主要参考协议RFC2616来完成。

  1 package Web;
  2 
  3 import java.awt.image.BufferedImage;
  4 import java.io.ByteArrayOutputStream;
  5 import java.io.IOException;
  6 
  7 import javax.imageio.ImageIO;
  8 import javax.servlet.http.HttpServletResponse;
  9 
 10 import org.apache.poi.hssf.usermodel.HSSFCell;
 11 import org.apache.poi.hssf.usermodel.HSSFRow;
 12 import org.apache.poi.hssf.usermodel.HSSFSheet;
 13 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 14 import org.springframework.stereotype.Controller;
 15 import org.springframework.web.bind.annotation.RequestMapping;
 16 import org.springframework.web.bind.annotation.ResponseBody;
 17 
 18 /**
 19  * 发送图片的控制器
 20  * @author yangchaolin
 21  *
 22  */
 23 @Controller
 24 @RequestMapping("/demo")
 25 public class ImageController {
 26     /**
 27      * @ResponseBody 注解会自动处理控制返回值
 28      * 1 当返回的类型为JavaBean(数组,集合),返回的数据为JSON
 29      * 2 当返回的类型为byte数组,直接将byte数组装入响应消息的body
 30      * 3 produces的配置相当如设置消息头Content-Type为image/png
 31      */
 32     @RequestMapping(value="/imgController.do",produces="image/png")
 33     @ResponseBody
 34     public byte[] sendImageToWeb() throws IOException {
 35         byte[] image=createImage();
 36         return image;        
 37     }
 38     
 39     /**
 40      * 下载图片需参考RFC2616 19.5.1 Content-Disposition
 41      * 如果想下载图片,需要将Content-Type设置为application/octet-stream
 42      * 另外还需要在响应消息头设置Content-Disposition
 43      */
 44     @RequestMapping(value="/downloadImage.do",produces="application/octet-stream")
 45     @ResponseBody
 46     public byte[] downloadImageForWeb(HttpServletResponse response) throws IOException {
 47         //example写法: Content-Disposition: attachment; filename="fname.ext"
 48         response.setHeader("Content-Disposition", "attachment; filename="text.png"");// " 转义 "
 49         return createImage();        
 50     }
 51     
 52     /**
 53      * 生成excel,浏览器可以下载,类似图片下载
 54      */
 55     @RequestMapping(value="/downloadExcel.do",produces="application/octet-stream")
 56     @ResponseBody
 57     public byte[] downloadExcelForWeb(HttpServletResponse response) throws IOException {
 58         //example写法: Content-Disposition: attachment; filename="fname.ext"
 59         response.setHeader("Content-Disposition", "attachment; filename="text.xls"");// " 转义 "
 60         return createExcel();        
 61     }
 62     
 63     /**
 64      * 生成一个简单excel,并返回byte数组
 65      */
 66     private byte[] createExcel() throws IOException {
 67         //生成工作簿
 68         HSSFWorkbook workbook=new HSSFWorkbook();
 69         //生成工作表
 70         HSSFSheet sheet=workbook.createSheet("information");
 71         //创建一行
 72         HSSFRow row=sheet.createRow(0);//第一行
 73         //行中创建一个Cell
 74         HSSFCell cell=row.createCell(0);//第一列
 75         //Cell中写入数据
 76         cell.setCellValue("hello excel with poi");
 77         //将Excel数据写入到输出流
 78         ByteArrayOutputStream os=new ByteArrayOutputStream();
 79         workbook.write(os);
 80         os.close();
 81         //使用输出流API,转换成byte数组并返回
 82         byte[] excel=os.toByteArray();        
 83         return excel;
 84     }
 85 
 86     /**
 87      * 绘制一张图片,返回图片编码后的byte数组
 88      */
 89     public byte[] createImage() throws IOException {
 90         //使用BufferedImage绘图
 91         //第一个参数图片宽度
 92         //第二个参数图片高度
 93         //第三个参数图片像素颜色表示方式,为3位的byte,一个byte调节一种颜色,分别为BGR,一个byte为8位,可以代表0-255的亮度
 94         BufferedImage buffImage=new BufferedImage(100,100,BufferedImage.TYPE_3BYTE_BGR);
 95         //设置图片某个像素点的颜色
 96         buffImage.setRGB(50, 50, 0x00ff00);//使用16进制表示
 97         ByteArrayOutputStream os=new ByteArrayOutputStream();
 98         //图片编码为png
 99         ImageIO.write(buffImage, "png", os);
100         //关闭流
101         os.close();
102         byte[] image=os.toByteArray();
103         return image;
104     }
105 
106 }

网页部分:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8">
 5 <title>web发送请求到服务端得到图片</title>
 6 </head>
 7 <body style="font-family:'微软雅黑'">
 8 
 9   <h1>以下为使用servlet从服务端得到的图片</h1>
10   <p>
11     <img alt="" src="demoImg"> <!-- demoImg为web.xml中配置好的访问路径 -->
12   </p>
13 
14   <h1>以下为使用Spring MVC从服务端得到的图片</h1>
15   <p>
16     <img alt="" src="demo/imgController.do"> <!-- src里就是get请求地址 -->
17   </p>
18   
19   <h1>以下为使用Spring MVC从服务器下载图片</h1> <!-- 下载图片,需参考RFC2616 19.5.1里的要求 -->
20   <a href="demo/downloadImage.do">下载图片</a>
21   
22   <h1>以下为使用Spring MVC从服务器下载Excel</h1> <!-- 下载Excel,需参考RFC2616 19.5.1里的要求,类似图片下载,最后都是返回byte数组 -->
23   <a href="demo/downloadExcel.do">下载Excel</a>
24
33 </body>
34 </html>

网页显示:

下载的图片和Excel在文件夹中:

RFC1867协议参考

文件的上传,参考文档 Form-based File Upload in HTML RFC1867,字面意思为基于Form表单的文件上传,其中在文档目录 2.HTML forms with file submission 里有一个example,示例用户如何通过表单上传文件。

<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST>

 File to process: <INPUT NAME="userfile1" TYPE="file">

 <INPUT TYPE="submit" VALUE="Send File">

</FORM>

发送的请求,必须为POST请求,此外还需要完成以下两步才能正常上载文件:

1 导入一个commons-fileupload包

2 在web.xml中进行解析器MultipartResolver的配置

解析器的配置方法参考spring-framework的文档目录 22.10.2 Using a MultipartResolver with Commons FileUpload,本文档使用版本4

使用Spring-MVC实现文件上传

使用Spring-MVC实现文件的上传,网页端除了使用传统Form表单提交方式,还可以使用AJAX的FormData来提交,前者是上传后会刷新页面,后者是不刷新页面。本项目中上传文件成功后,服务器将给浏览器发送一个Map,里面包含上传成功的信息,不再采用云笔记的JasonResult来返回数据,代码如下。

  1 package Web;
  2 
  3 import java.io.File;
  4 import java.io.FileOutputStream;
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.util.HashMap;
  8 import java.util.Map;
  9 
 10 import org.springframework.stereotype.Controller;
 11 import org.springframework.web.bind.annotation.RequestMapping;
 12 import org.springframework.web.bind.annotation.ResponseBody;
 13 import org.springframework.web.multipart.MultipartFile;
 14 
 15 /**
 16  * 上传文件的控制器
 17  * @author yangchaolin
 18  *
 19  */
 20 @Controller
 21 @RequestMapping("/file")
 22 public class UploadController {
 23     
 24     @RequestMapping("/uploadFile.do")
 25     @ResponseBody
 26     public Object uploadFile(MultipartFile userfile1,MultipartFile userfile2) throws IllegalStateException, IOException{
 27         /**
 28          * Spring MVC中可以使用MultipartFile接受上载的文件,文件中的一切数据都可以从此对象中获取
 29          * 比如可以获取文件原始名,文件类型等
 30          */
 31         
 32         //比如获取上载文件的原始文件名,就是文件系统中的文件名
 33         String filename1=userfile1.getOriginalFilename();
 34         String filename2=userfile2.getOriginalFilename();
 35         System.out.println("文件1原始文件名为:"+filename1);
 36         System.out.println("文件2原始文件名为:"+filename2);
 37         
 38         Map<String,String> map=new HashMap<String,String>();
 39         
 40         /**
 41          * 保存上传的文件有三种方法:
 42          * 1 MultipartFile接口的transferTo(File dest)方法
 43          * 将文件直接保存到目标文件,适用于大文件
 44          * 2 MultipartFile接口的getBytes()方法
 45          * 将文件全部读取,返回byte数组,保存在内存,适用于小文件,大文件有爆内存风险
 46          * 3 MultipartFile接口的getInputStream()方法,将文件读取后返回一个InputStream
 47          * 获取上载文件的流,适用于大文件  
 48          */
 49             
 50         //mac中保存文件地址 /Users/yangchaolin
 51         //window中保存地址 D:/yangchaolin
 52         //linux中保存地址 /home/soft01/yangchaolin
 53         
 54         //1 使用transferTo(File dest)            
 55         // 创建目标文件夹
 56         File dir=new File("F:/FileUploadAndDownload");
 57         boolean makeDirectoryResult=dir.mkdirs();
 58         System.out.println("文件夹路径是否建立:"+makeDirectoryResult);
 59         //往文件夹放第一个文件
 60         File file=new File(dir,filename1);    
 61         userfile1.transferTo(file);
 62         
 63         /**
 64          * transferTo方法如果不注释掉,后面执行第二种方法写入文件到硬盘会报错
 65          * 报错内容:java.lang.IllegalStateException: File has been moved - cannot be read again
 66          * 原因为transferTo方法底层在执行时,会检查需要写入到OutputStream的文件字节数是否超过MultipartResolver配置的大小,
 67          * 默认设置为10kib,如果超过了,执行完这个方法后会从内存中删除上传的文件,后面再想读取就会报错
 68          */
 69         
 70         //2 使用getInputStream()方法
 71         File file1=new File(dir,filename1);
 72         InputStream isWithoutBuff=userfile1.getInputStream();
 73         //使用FileoutputStream写出到文件
 74         FileOutputStream fosWithoutbuff=new FileOutputStream(file1);
 75         //InputStream一个字节一个字节的读取,将读取到的结果写入到FileOutputStream
 76         int b;//读取一个byte后,以int类型显示数值,范围0~255        
 77         while((b=isWithoutBuff.read())!=-1) {
 78             //read()方法每次只读取文件的一个byte
 79             fosWithoutbuff.write(b);
 80         }
 81         isWithoutBuff.close();
 82         fosWithoutbuff.close();
 83         
 84         //同样使用InputStream读取,将读取到的结果写入到FileOutputStream,但使用了缓冲字节数组
 85         File file2=new File(dir,filename2);
 86         InputStream isWithBuff=userfile2.getInputStream();
 87         FileOutputStream fosWithBuff=new FileOutputStream(file2);
 88         int n;//保存返回读取到的字节数, 一次8192个字节,当不够时就是实际读取到的字节数
 89         byte[] buff=new byte[8*1024];//8kib的缓冲字节数组
 90         while((n=isWithBuff.read(buff))!=-1) {
 91             System.out.println("读取后的字节数:"+n);
 92             fosWithBuff.write(buff, 0, n);
 93         }
 94         isWithBuff.close();
 95         fosWithBuff.close();
 96         
 97         //3 使用getBytes()方法
 98         byte[] data=userfile2.getBytes();
 99         //写出byte数组到文件
100         File file3=new File(dir,filename2);
101         FileOutputStream fosWithByte=new FileOutputStream(file3);
102         fosWithByte.write(data,0,data.length);
103         fosWithByte.close();
104         
105         map.put("Result", "upload Success");        
106 
107         return map;//需要导入jackson的三个核心包,否则无法正常转换成JSON
108         
109     }
110 
111 }

传统Form表单提交:

1   <h1>以下为使用Spring MVC上载图片</h1>
2   <form method="post" action="file/uploadFile.do" enctype="multipart/form-data">
3     file to process:<br>
4     <input type="file" name="userfile1"/> <!-- name用于提交到服务器的表单数据进行标识 -->
5     <input type="file" name="userfile2"/>
6     <input type="submit" value="upload文件"/>
7   </form>

网页显示:

文件上传结果:

AJAX提交的方式,服务端无需修改,只需要修改浏览器端代码即可:

AJAX文件上载,参考项目FileUploadAndDownLoad ,AJAX上传适用于最新的浏览器版本,老版本的可能不兼容,使用FormData()来上传文件,FormData类似于Form,对于前面Spring MVC上传文件的方式:

1 不使用AJAX,客户端需使用form来实现文件上传,form表单里有input元素,通过input元素的name属性来标识上传文件的资源,利用commons-fileupload包,客户端上传的文件会保存在MultipartFile对象中,然后可再通过三种方式,将上传的文件写入到硬盘。

2 使用AJAX,客户端不需要写form表单,文件资源将直接保存到FormData里面,然后传入服务器,服务端的处理跟前面第一种没有区别,只是保存到FormData里的标识符,需要跟服务端方法里的参数名字一样。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8">
 5 <title>使用AJAX上传文件</title>
 6 <script type="text/javascript" src="scripts/jquery.min.js"></script>
 7 <script type="text/javascript">
 8  <!--使用AJAX来上传数据-->
 9  $(function(){
10      $("#uploadFile").bind("click",uploadFileWithAJAX);     
11  });
12  function uploadFileWithAJAX(){
13      //获取文件,将文件保存到FormData里
14      var file1=$("#file1")[0].files[0];//jQuery对象转换成DOM对象,然后使用DOM对象的files属性,获取文件
15      var file2=$("#file2")[0].files[0];
16      //创建内存中的表单对象
17      var formdata=new FormData();
18      //第一个参数,必须和服务端MultipartFile的参数名一样
19      formdata.append("userfile1",file1);
20      formdata.append("userfile2",file2);
21      
22      //发送ajax请求
23      $.ajax({
24          url:"file/uploadFile.do",
25          type:"POST",
26          data:formdata,
27          dataType:"JSON",
28          cache:false,
29          processData:false,
30          contentType:false,
31          success:function(resultData){
32              console.log(resultData);
33              if(resultData.Result=="upload Success"){
34                  $("#result").text("upload Success");
35              }
36          },
37          error:function(e){
38              console.log("网络错误");
39          }         
40      });
41      
42  }
43 </script>
44 </head>
45 <body>
46 
47   <h1>以下为使用AJAX上传文件,服务端代码不用变</h1>
48   <input type="file" id="file1"><br>
49   <input type="file" id="file2"><br>
50   <input type="submit" value="上传图片" id="uploadFile"><br>
51   <div id="result"></div>
52   
53 </body>
54 </html>

网页显示:

文件上传结果:

总结

(1)无论是文件的上传,下载还是显示,都需要参考协议,其中上传参考RFC1867,下载参考RFC2616,此外还需要参考Spring-Framework的文档

(2)使用Spring注解@ResponseBody不仅仅可以返回JSON,还可以返回byte数组

(3)文件的上传可以使用AJAX,并使用FormData完成文件上传

原文地址:https://www.cnblogs.com/youngchaolin/p/10700640.html