云笔记项目- 文件的上传与下载学习
发布日期:2021-08-15 20:52:00 浏览次数:1 分类:技术文章

本文共 17478 字,大约阅读时间需要 58 分钟。

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

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

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

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

(3)Spring-framework参考文档:

  其他协议:

(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 
2
4.0.0
3
com.boe
4
FileUploadAndDownLoad
5
0.0.1-SNAPSHOT
6
war
7 8
9
10
11
org.springframework
12
spring-webmvc
13
4.2.3.RELEASE
14
15 16
17
org.springframework
18
spring-webmvc
19
4.2.3.RELEASE
20
sources
21
22 23
24
org.springframework
25
spring-webmvc
26
4.2.3.RELEASE
27
javadoc
28
29 30
31
32
org.apache.poi
33
poi
34
3.17
35
36 37
38
org.apache.poi
39
poi-ooxml
40
3.17
41
42 43
44
45
commons-fileupload
46
commons-fileupload
47
1.3
48
49 50
51
52
53
com.fasterxml.jackson.core
54
jackson-core
55
2.2.3
56
57 58
59
60
com.fasterxml.jackson.core
61
jackson-databind
62
2.2.3
63
64 65
66
67
com.fasterxml.jackson.core
68
jackson-annotations
69
2.2.3
70
71 72
73 74

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

1         
2
3
4
5 6
7
8
9
10
11
12 13
14
15

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

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

1 
2
3
FileUploadAndDownLoad
4
5
index.html
6
index.htm
7
index.jsp
8
default.html
9
default.htm
10
default.jsp
11
12
13
14
15
ImageServlet
16
ImageServlet
17
Web.ImageServlet
18
19
20
ImageServlet
21
/demoImg
22
23
24
25
26
27
DispatcherServlet
28
DispatcherServlet
29
org.springframework.web.servlet.DispatcherServlet
30
31
32
contextConfigLocation
33
classpath:config/spring-*.xml
34
35
1
36
37
38
DispatcherServlet
39
*.do
40
41

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

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 stub18     }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      * @return35      * @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         //图片编码为png47         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  2  3  4 
5 web发送请求到服务端得到图片 6 7 8 9

以下为使用servlet从服务端得到的图片

10

11

12

13 14

以下为使用Spring MVC从服务端得到的图片

15

16

17

18 19

以下为使用Spring MVC从服务器下载图片

20 下载图片21 22

以下为使用Spring MVC从服务器下载Excel

23 下载Excel2433 34

网页显示:

下载的图片和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
map=new HashMap
(); 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的三个核心包,否则无法正常转换成JSON108 109 }110 111 }

传统Form表单提交:

1   

以下为使用Spring MVC上载图片

2
3 file to process:
4
5
6
7

网页显示:

文件上传结果:

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  2  3  4 
5 使用AJAX上传文件 6 7 44 45 46 47

以下为使用AJAX上传文件,服务端代码不用变

48
49
50
51
52 53 54

网页显示:

文件上传结果:

总结

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

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

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

转载于:https://www.cnblogs.com/youngchaolin/p/10700640.html

转载地址:https://blog.csdn.net/weixin_30779691/article/details/98095826 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【转载】String、StringBuffer与StringBuilder之间区别
下一篇:Linux流量监控工具 - iftop (最全面的iftop教程)

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年03月23日 08时46分14秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

linux查看wifi信号命令_linux – 获取WIFI信号强度 – 寻求最佳方式(IOCTL,iwlist(iw)等)... 2019-04-21
npm 不重启 全局安装后_解决修复npm安装全局模块权限的问题 2019-04-21
vs格式化json 不生效_vs code 格式化 json 配置 2019-04-21
go 字符串反序列化成对象数组_Fastjson 1.2.24反序列化漏洞深度分析 2019-04-21
onmessage websocket 收不到信息_WebSocket断开重连解决方案,心跳重连实践 2019-04-21
hibernate mysql 缓存_hibernate和mysql的缓存问题,没辙了! 2019-04-21
abp框架 mysql_ABP框架使用Mysql数据库 2019-04-21
mysql树形递归删除_使用递归删除树形结构的所有子节点(java和mysql实现) 2019-04-21
linux mysql 不能连接远程_linux mysql 远程连接 2019-04-21
mysql $lt_mongodb中比较级查询条件:($lt $lte $gt $gte)(大于、小于)、查找条件... 2019-04-21
install python_Install python on AIX 7 2019-04-21
jquery查找div下第一个input_jquery查找div元素第一个元素id 2019-04-21
如何修改手机屏幕显示的长宽比例_屏幕分辨率 尺寸 比例 长宽 如何计算 2019-04-21
mysql 的版本 命名规则_MySQL版本和命名规则 2019-04-21
no java stack_Java Stack contains()用法及代码示例 2019-04-21
java动态代码_Java Agent入门学习之动态修改代码 2019-04-21
python集合如何去除重复数据_Python 迭代删除重复项,集合删除重复项 2019-04-21
iview 自定义时间选择器组件_Vue.js中使用iView日期选择器并设置开始时间结束时间校验功能... 2019-04-21
java 验证码校验_JavaWeb验证码校验功能代码实例 2019-04-21
java多线程初学者指南_Java多线程初学者指南(4):线程的生命周期 2019-04-21