一、文件上传
在实际的项目开发中,文件的上传和下载可以说是最常用的功能之一,例如个人头像图片的上传与下载、邮件附件的上传和下载等。本节我们将对 Spring MVC 中的文件上传功能进行讲解。
随着我们互联网的发展,我们的用户从直接访问网站获取信息。变为希望将自己本地的资源发送给服务器,让服务器提供给其他人使用或者查看。还有部分的用户希望可以将本地的资源上传服务器存储起来,然后再其他的电脑中可以通过访问网站来获取上传的资源,这样用户就可以打破空间的局限性,再任何时候只要有网有电脑就可以对自己的资源进行操作,比如:云存储,云编辑
1.如何在页面中显示一个按钮?
- 用户可以点击该按钮后选择本地要上传的文件在页面中使用input标签,type值设置为”file”即可
2.确定上传请求的发送方式
- 上传成功后的响应结果在当前页面显示,使用ajax请求来完成资源的发送
3.上传请求的请求数据及其数据格式
- 请求数据:上传的文件本身普通数据:用户名,Id,密码等,建议上传功能中不携带除上传资源以外的数据
- 数据格式:传统的请求中,请求数据是以键值对的格式来发送给后台服务器的,但是在上传请求中,没有任何一个键可以描述上次的数据,因为数据本身是非常大的键就相当于一个变量,我们使用一个变量存储一个10g的电影显然是不可能 的。在上传请求中,将请求数据以二进制流的方式发送给服务器。
4.在ajax中如何发送二进制流数据给服务器
- 创建FormData的对象,将请求数据存储到该对象中发送
- 将processData属性的值设置为false,告诉浏览器发送对象请求数据
- 将contentType属性的值设置为false,设置请求数据的类型为二进制类型。
- 正常发送ajax即可
5.上传成功后后台服务器应该响应什么结果给浏览器
- 并且浏览器如何处理后台服务器处理完成后,响应一个json对象给浏览器,示例格式如: { state:true,msg:“服务器繁忙”,url:”上传成功的资源的请求地址”}
6.文件上传依赖的jar
其他的依赖跟上一个章节一致
<!--文件上传依赖--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency>
7.配置文件上传组件
这个在springmvc.xml文件中配置
<!--文件上传解析组件 id必须为multipartResolver springmvc默认使用该id找该组件 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
8.准备用户表
准备数据,作为后期保存数据使用
DROP TABLE IF EXISTS `player`; CREATE TABLE `player` ( `id` int(25) NOT NULL AUTO_INCREMENT COMMENT '编号', `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '账号', `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码', `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称', `photo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像', `filetype` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '类型', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
9.前端代码
前端利用了jquery完成,这里只是完成了图片的上传,并没有完成回显
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> /*function ()这是页面加载函数*/ $(function () { /*获取id为uploadFile的元素*/ $("#uploadFile").click(function () { //获取需要上传的文件 根据input标签的id属性获取 var photoFile = $("#photo")[0].files[0] console.log(photoFile) /*如果不上传文件,没有选中,photoFile则是一个没有内容的为undefined,所以要进行判断*/ if(photoFile==undefined){ alert("您还未选择图片") return; } //将文件转换成formData对象 var formData =new FormData(); //这里添加后的时候,是一个kv的形式 value是上传的文件,key是后台的一个controller控制器方法的参数名相同multipartFile formData.append("multipartFile",photoFile) //通过Ajax向后台发送文件 $.ajax({ type:"post",/*类型一定要使用post,因为图片转为二进制传递,get无法传递二进制*/ data:formData,/*提交的图片数据*/ url:"fileUpload.do", /*提交给后台的controller地址*/ /*为false表示为后台提交的是一个对象,而不是字符串*/ processData:false, /*设置为false表示请求的数据为二进制类型*/ contentType:false, success:function (result) { //接受后台的响应result接受响应 //图片回显 } }) //接受后台的 }) }) </script> </head> <body> <form method="post" action="addPlayer"> <p>账户:<input type="text" name="name"></p> <p>密码:<input type="password" name="password"></p> <p>昵称:<input type="text" name="nickname"></p> <p>头像:<br> <%--这个id是为了后端获取提交数据的--%> <input id="photo" type="file"> <%--javascript:void(0)取消超链接功能--%> <a id="uploadFile" href="javascript:void(0)">立即上传</a> </p> <p><input type="submit" value="注册"></p> </form> </body> </html>
10.controller代码
这里将文件保存到了本地磁盘中
package com.augus.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; @Controller public class FileUploadController { /** * 打开页面 * @return */ @RequestMapping("/register") public String testRegister(){ return "index"; } @ResponseBody @RequestMapping("/fileUpload.do") public String fileUpload(MultipartFile multipartFile, HttpServletRequest request) throws IOException { //指定文件存储路径 File file = new File("d:/images"); //获取文件名 String originalFilename = multipartFile.getOriginalFilename(); //文件保存位置和需要保存的文件名称 File file1 = new File(file, originalFilename); //文件保存 multipartFile.transferTo(file1); return "ok"; } }
11.测试
选择图片,点击 立即上传,即可在路径下面看到上传的文件
点击立即上传即可看到上传的图片
二、中文文件名编码和文件存储路径问题
上面实现的文件上传中的几个问题:
- 中文文件名编码问题:已经通过过滤器解决
- 文件位置存储问题:放在当前项目下,作为静态资源,这样可以通过URL访问
这里注意上传的文件是在编译后的文件中,并不是你的代码目录中
@Controller public class FileUploadController { /** * 打开页面 * @return */ @RequestMapping("/register") public String testRegister(){ return "index"; } @ResponseBody @RequestMapping("/fileUpload.do") public String fileUpload(MultipartFile multipartFile, HttpServletRequest request) throws IOException { //指定文件存储路径为项目部署环境下的upload目录(编译后的) String realPath = request.getServletContext().getRealPath("/upload"); System.out.println(realPath); File filePath = new File(realPath); //如果不存在则创建目录 if(!filePath.exists()){ filePath.mkdirs(); } //获取文件名 String originalFilename = multipartFile.getOriginalFilename(); //文件保存位置和需要保存的文件名称 File file1 = new File(filePath, originalFilename); //文件保存 multipartFile.transferTo(file1); return "ok"; } }
在SpringMVC中配置静态资源放行
<mvc:resources mapping="/upload/**" location="/upload/"></mvc:resources>
重新编译启动项目,上传图片,在编译后的项目中即可看到上传的图片
可以通过URL地址直接访问上传的图片http://localhost:8080/springmvc_test04_war_exploded/upload/1.jpg
三、文件名冲突问题
目前上传文件后,如果再次上传同名文件,就会出现覆盖,这时候就要使用UUID对文件名进行重命名,关于UUID说明如下:
UUID全称:Universally Unique Identifier,即通用唯一识别码。 UUID是由一组32位数的16进制数字所构成,是故UUID理论上的总数为16^32 = 2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。 UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符,如:550e8400-e29b-41d4-a716-446655440000。
代码如下:
@Controller public class FileUploadController { /** * 打开页面 * @return */ @RequestMapping("/register") public String testRegister(){ return "index"; } /** * 处理ajax发送的请求。保存文件 * @param multipartFile * @param request * @return * @throws IOException */ @ResponseBody @RequestMapping("/fileUpload.do") public String fileUpload(MultipartFile multipartFile, HttpServletRequest request) throws IOException { //指定文件存储路径为项目部署环境下的upload目录(编译后的) String realPath = request.getServletContext().getRealPath("/upload"); System.out.println(realPath); File filePath = new File(realPath); //如果不存在则创建目录 if(!filePath.exists()){ filePath.mkdirs(); } //获取文件名 String originalFilename = multipartFile.getOriginalFilename(); //避免文件名冲突,使用UUID替换文件名 String uuid = UUID.randomUUID().toString(); //获取图片后缀名 例如.jpg // lastIndexOf(".")截取文件中最后一个点,防止有多个. String photoType = originalFilename.substring(originalFilename.lastIndexOf(".")); //组合成新的文件名 String fileName = uuid.concat(photoType); //文件保存位置和需要保存的文件名称 File file1 = new File(filePath, fileName); //文件保存 multipartFile.transferTo(file1); return "ok"; } }
在之前的代码基础上,主要是添加如下内容
四、控制文件类型和控制文件大小
对于上传的文件大小和文件类型比较加以控制,这里在controller中完成即可
@Controller public class FileUploadController { /** * 打开页面 * @return */ @RequestMapping("/register") public String testRegister(){ return "index"; } /** * 处理ajax发送的请求。保存文件 * @param multipartFile * @param request * @return * @throws IOException */ @ResponseBody @RequestMapping("/fileUpload.do") public Map<String,String> fileUpload(MultipartFile multipartFile, HttpServletRequest request) throws IOException { HashMap<String, String> map = new HashMap<String, String>(); //控制文件大小 if(multipartFile.getSize()>1024*1024*5){ map.put("message","文件大小不能超过5M"); return map; } //指定文件存储路径为项目部署环境下的upload目录(编译后的) String realPath = request.getServletContext().getRealPath("/upload"); System.out.println(realPath); File filePath = new File(realPath); //如果不存在则创建目录 if(!filePath.exists()){ filePath.mkdirs(); } //获取文件名 String originalFilename = multipartFile.getOriginalFilename(); //避免文件名冲突,使用UUID替换文件名 String uuid = UUID.randomUUID().toString(); //获取图片后缀名 例如.jpg // lastIndexOf(".")截取文件中最后一个点,防止有多个. String photoType = originalFilename.substring(originalFilename.lastIndexOf(".")); //控制文件类型 if(!photoType.equals(".jpg")){ map.put("message", "文件类型必须是.jpg"); return map; } //组合成新的文件名 String fileName = uuid.concat(photoType); //文件保存位置和需要保存的文件名称 File file1 = new File(filePath, fileName); //文件保存 multipartFile.transferTo(file1); // 上传成功之后,把文件的名字和文件的类型返回给浏览器 map.put("message", "上传成功"); map.put("fileName", fileName); map.put("fileType", multipartFile.getContentType()); return map; } }
也可以在springmvc配置文件中控制上传文件的大小,但是不推荐
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--设置上传文件的默认编码格式--> <property name="defaultEncoding" value="UTF-8"/> <!--在这里控制上传文件的大小 不推荐 这时候没办法给前端返回提示信息,推荐在controller中配置--> <!--<property name="maxUploadSize" value="10240000000"></property>--> </bean>
五、上传图片回显问题
后端已经将图片的文件名响应给浏览器,修改前端代码只要是一下两部分内容:
代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> /*function ()这是页面加载函数*/ $(function () { /*获取id为uploadFile的元素*/ $("#uploadFile").click(function () { //获取需要上传的文件 根据input标签的id属性获取 var photoFile = $("#photo")[0].files[0] console.log(photoFile) /*如果不上传文件,没有选中,photoFile则是一个没有内容的为undefined,所以要进行判断*/ if(photoFile==undefined){ alert("您还未选择图片") return; } //将文件转换成formData对象 var formData =new FormData(); //这里添加后的时候,是一个kv的形式 value是上传的文件,key是后台的一个controller控制器方法的参数名相同multipartFile formData.append("multipartFile",photoFile) //通过Ajax向后台发送文件 $.ajax({ type:"post",/*类型一定要使用post,因为图片转为二进制传递,get无法传递二进制*/ data:formData,/*提交的图片数据*/ url:"fileUpload.do", /*提交给后台的controller地址*/ /*为false表示为后台提交的是一个对象,而不是字符串*/ processData:false, /*设置为false表示请求的数据为二进制类型*/ contentType:false, success:function (result) { //接受后台的响应result接受响应 alert(result.message) //图片回显 根据id找到图片显示地址,为img,然后设置src属性的值,在取响应回来的数据 $("#headImage").attr("src","upload/"+result.fileName) } }) //接受后台的 }) }) </script> </head> <body> <form method="post" action="addPlayer"> <p>账户:<input type="text" name="name"></p> <p>密码:<input type="password" name="password"></p> <p>昵称:<input type="text" name="nickname"></p> <p>头像:<br> <%--这个id是为了后端获取提交数据的--%> <input id="photo" type="file"><br> <%--图片回显展示--%> <img id="headImage" style="width: 200px;height: 200px" alt="你还未上传图片"><br> <%--javascript:void(0)取消超链接功能--%> <a id="uploadFile" href="javascript:void(0)">立即上传</a> </p> <p><input type="submit" value="注册"></p> </form> </body> </html>
六.进度条问题
在上传文件的时候一般情况下都会设置进度条,主要是三部分:
前端代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <style> .progress { width: 200px; height: 10px; border: 1px solid #ccc; border-radius: 10px; margin: 10px 0px; overflow: hidden; } /* 初始状态设置进度条宽度为0px */ .progress > div { width: 0px; height: 100%; background-color: yellowgreen; transition: all .3s ease; } </style> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> /*function ()这是页面加载函数*/ $(function () { /*获取id为uploadFile的元素*/ $("#uploadFile").click(function () { //获取需要上传的文件 根据input标签的id属性获取 var photoFile = $("#photo")[0].files[0] console.log(photoFile) /*如果不上传文件,没有选中,photoFile则是一个没有内容的为undefined,所以要进行判断*/ if(photoFile==undefined){ alert("您还未选择图片") return; } //将文件转换成formData对象 var formData =new FormData(); //这里添加后的时候,是一个kv的形式 value是上传的文件,key是后台的一个controller控制器方法的参数名相同multipartFile formData.append("multipartFile",photoFile) //通过Ajax向后台发送文件 $.ajax({ type:"post",/*类型一定要使用post,因为图片转为二进制传递,get无法传递二进制*/ data:formData,/*提交的图片数据*/ url:"fileUpload.do", /*提交给后台的controller地址*/ /*为false表示为后台提交的是一个对象,而不是字符串*/ processData:false, /*设置为false表示请求的数据为二进制类型*/ contentType:false, success:function (result) { //接受后台的响应result接受响应 alert(result.message) //图片回显 根据id找到图片显示地址,为img,然后设置src属性的值,在取响应回来的数据 $("#headImage").attr("src","upload/"+result.fileName) },xhr:function () { var xhr = new XMLHttpRequest(); //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件 xhr.upload.addEventListener("progress",function (e) { //loaded代表上传了多少 //total代表总数为多少 var progressRate = (e.loaded/e.total)*100+"%"; //通过设置进度条的宽度达到效果 $(".progress>div").css("width",progressRate); }) return xhr; } }) //接受后台的 }) }) </script> </head> <body> <form method="post" action="addPlayer"> <p>账户:<input type="text" name="name"></p> <p>密码:<input type="password" name="password"></p> <p>昵称:<input type="text" name="nickname"></p> <p>头像:<br> <%--这个id是为了后端获取提交数据的--%> <input id="photo" type="file"><br> <%--图片回显展示--%> <img id="headImage" style="width: 200px;height: 200px" alt="你还未上传图片"><br> <%--进度条--%> <div class="progress"> <div></div> </div> <%--javascript:void(0)取消超链接功能--%> <a id="uploadFile" href="javascript:void(0)">立即上传</a> </p> <p><input type="submit" value="注册"></p> </form> </body> </html>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/253655.html