• SpringMVC 文件上传下载


    @

    文件上传

    文件上传是项目开发中最常见的功能。为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

    一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

    Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。而Spring MVC则提供了更简单的封装。
    Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

    MultipartFile对象

    Spring MVC会将上传的文件绑定到MultipartFile对象中。MultipartFile提供了获取上传文件内容、文件名等方法。通过transferTo()方法还可以将文件存储到硬件中,MultipartFile对象中的常用方法如下:

    • byte[] getBytes():获取文件数据
    • String getContentType[]:获取文件MIME类型,如image/jpeg等
    • InputStream getInputStream():获取文件流
    • String getName():获取表单中文件组件的名字
    • String getOriginalFilename():获取上传文件的原名
    • Long getSize():获取文件的字节大小,单位为byte
    • boolean isEmpty():是否有上传文件
    • void transferTo(File dest):将上传文件保存到一个目录文件中

    文件下载

    在页面给出了一个超链接,该链接href的属性等于要下载文件的文件名,就可以实现文件下载了。

    接收页面传递的文件名filename后,使用Apache Commons FileUpload组件的FileUtils读取项目的上传文件,并将其构建成ResponseEntity对象返回客户端下载。

    使用ResponseEntity对象,可以很方便的定义返回的HttpHeaders和HttpStatus。上面代码中的MediaType,代表的是Internet Media Type,即互联网媒体类型,也叫做MIME类型。在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。HttpStatus类型代表的是Http协议中的状态。有关MediaType和HttpStatus类可以参考Spring MVC的API文档。

    上传下载示例

    pom.xml增加

    <!-- 文件上传组件 -->
    <dependency>
    	<groupId>commons-fileupload</groupId>
    	<artifactId>commons-fileupload</artifactId>
    	<version>1.3</version>
    </dependency>
    

    创建uploadForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    	pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>文件上传</title>
    </head>
    <body>
    	<h2>文件上传</h2>
    	<form action="${pageContext.request.contextPath}/mvc/upload" enctype="multipart/form-data" method="post">
    		<table>
    			<tr>
    				<td>文件描述:</td>
    				<td><input type="text" name="description"></td>
    			</tr>
    			<tr>
    				<td>请选择文件:</td>
    				<td><input type="file" name="file"></td>
    			</tr>
    			<tr>
    				<td><input type="submit" value="上传"></td>
    			</tr>
    		</table>
    	</form>
    </body>
    </html>
    

    创建uploadForm2.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>用户注册</title>
    </head>
    <body>
    	<h2>用户注册</h2>
    	<form action="${pageContext.request.contextPath}/mvc/upload2" enctype="multipart/form-data" method="post">
    		<table>
    			<tr>
    				<td>用户名:</td>
    				<td><input type="text" name="name"></td>
    			</tr>
    			<tr>
    				<td>请上传头像:</td>
    				<td><input type="file" name="image"></td>
    			</tr>
    			<tr>
    				<td><input type="submit" value="注册"></td>
    			</tr>
    		</table>
    	</form>
    </body>
    </html>
    

    创建userInfo.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>文件下载</title>
    </head>
    <body>
    	<h3>文件下载</h3>
    	<a href="download?filename=${requestScope.user.image.originalFilename}"> ${requestScope.user.image.originalFilename }</a>
    	<a href="download?filename=${user.name}"> ${user.name}</a>
    </body>
    </html>
    

    springmvc-servlet.xml添加

    SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver

    <!-- 文件上传的配置 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    	<property name="maxUploadSize" value="1048576" /><!-- 上传文件大小上限,单位为字节(1MB) -->
    	<property name="defaultEncoding" value="UTF-8" /><!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
    </bean>
    

    创建User2.java对象

    使用对象接受接收上传文件,必须要实现序列化接口

    package com.xc.entity;
    
    import java.io.Serializable;
    
    import org.springframework.web.multipart.MultipartFile;
    
    public class User2 implements Serializable {
    
    	private static final long serialVersionUID = 1L;
    	private String name;
    	private MultipartFile image;
    
    	public User2() {
    		super();
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public MultipartFile getImage() {
    		return image;
    	}
    
    	public void setImage(MultipartFile image) {
    		this.image = image;
    	}
    
    }
    
    

    创建UploadController.java

    package com.xc.controller;
    
    import java.io.File;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.io.FileUtils;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.MultipartFile;
    
    import com.xc.entity.User2;
    
    @Controller
    @RequestMapping("/mvc")
    public class UploadController {
    
    	// 上传文件会自动绑定到MultipartFile中
    	@RequestMapping(value = "/upload", method = RequestMethod.POST)
    	public String upload(HttpServletRequest request, @RequestParam("description") String description,
    			@RequestParam("file") MultipartFile multipartFile) throws Exception {
    
    		System.out.println(description);
    		if (!multipartFile.isEmpty()) {// 是否有上传文件
    			// 上传文件路径
    			// String path = request.getServletContext().getRealPath("/upload/");
    			// 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
    			String path = request.getServletContext().getRealPath("/WEB-INF/upload");
    			System.out.println(path);
    			String filename = multipartFile.getOriginalFilename();// 获取上传文件的原名
    			File filepath = new File(path, filename);
    			System.out.println(filepath);
    			// 判断路径是否存在,如果不存在就创建一个
    			if (!filepath.getParentFile().exists()) {
    				filepath.getParentFile().mkdirs();
    			}
    			multipartFile.transferTo(new File(path + File.separator + filename));// 将上传文件保存到一个目标文件当中
    			return "success";
    		} else {
    			return "error";
    		}
    	}
    
    	@RequestMapping(value = "/upload2")
    	public String register(HttpServletRequest request, @ModelAttribute User2 user, Model model) throws Exception {
    		System.out.println(user.getName());
    		// 如果文件不为空,写入上传路径
    		if (!user.getImage().isEmpty()) {
    			// 上传文件路径
    			String path = request.getServletContext().getRealPath("/WEB-INF/upload");
    			// 上传文件名
    			String filename = user.getImage().getOriginalFilename();
    
    			String fileExtName = filename.substring(filename.lastIndexOf(".") + 1);// 得到上传文件的扩展名
    			System.out.println("上传文件的扩展名:" + fileExtName);
    			String filenamePre = filename.substring(0, filename.lastIndexOf("."));
    			System.out.println("上传文件的文件名:" + filenamePre);
    
    			String saveFilename = makeFileName(filenamePre, fileExtName);
    			System.out.println("saveFilename:" + saveFilename);
    			Map<String, Object> pathMap = makePath(saveFilename, path);
    			String dir1 = pathMap.get("dir1").toString();
    			String dir2 = pathMap.get("dir2").toString();
    			System.out.println("pathMap:" + pathMap);
    			String realSavePath = pathMap.get("dir").toString();
    			System.out.println("realSavePath:" + realSavePath);
    			File filepath = new File(realSavePath, saveFilename);
    			// 判断路径是否存在,如果不存在就创建一个
    			if (!filepath.getParentFile().exists()) {
    				filepath.getParentFile().mkdirs();
    			}
    
    			// 将上传文件保存到一个目标文件当中
    			user.getImage().transferTo(new File(realSavePath + File.separator + saveFilename));
    			user.setName("" + dir1 + "/" + dir2 + "/" + saveFilename);
    			// 将用户添加到model
    			model.addAttribute("user", user);
    			return "userInfo";
    		} else {
    			return "error";
    		}
    	}
    
    	/**
    	 * @Method: makeFileName
    	 * @Description: 生成上传文件的文件名,文件的原始名称+"_"+uuid
    	 * @param filename
    	 *            文件的原始名称
    	 * @param fileExtName
    	 * @return 文件的原始名称+"_"+uuid
    	 */
    	private String makeFileName(String filenamePre, String fileExtName) { // 2.jpg
    		// 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
    		return filenamePre + "_" + UUID.randomUUID().toString() + "."+ fileExtName;
    	}
    
    	/**
    	 * 为防止一个目录下面出现太多文件,要使用hash算法打散存储
    	 * 
    	 * @Method: makePath
    	 * @param filename
    	 *            文件名,要根据文件名生成存储目录
    	 * @param savePath
    	 *            文件存储路径
    	 * @return 新的存储目录
    	 */
    	private Map<String, Object> makePath(String filename, String savePath) {
    		// 得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
    		int hashcode = filename.hashCode();
    		int dir1 = hashcode & 0xf; // 0--15
    		int dir2 = (hashcode & 0xf0) >> 4; // 0-15
    		// 构造新的保存目录
    		String dir = savePath + "\" + dir1 + "\" + dir2; // upload23 upload35
    		// File既可以代表文件也可以代表目录
    		File file = new File(dir);
    		// 如果目录不存在
    		if (!file.exists()) {
    			// 创建目录
    			file.mkdirs();
    		}
    
    		Map<String, Object> map = new HashMap<String, Object>();
    		map.put("dir", dir);
    		map.put("dir1", dir1);
    		map.put("dir2", dir2);
    		return map;
    	}
    
    	@RequestMapping(value = "/download")
    	public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("filename") String filename, Model model) throws Exception {
    		// 下载文件路径
    		String path = request.getServletContext().getRealPath("/WEB-INF/upload/");
    		File file = new File(path + File.separator + filename);
    		HttpHeaders headers = new HttpHeaders();
    		// 下载显示的文件名,解决中文名称乱码问题
    		String downloadFielName = new String(filename.getBytes("UTF-8"), "iso-8859-1");
    		// 通知浏览器以attachment(下载方式)打开图片
    		headers.setContentDispositionFormData("attachment", downloadFielName);
    		// application/octet-stream : 二进制流数据(最常见的文件下载)。
    		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    		return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
    	}
    
    }
    
    

    文件上传的细节

    上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的

    1. 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
    2. 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
    3. 为防止一个目录下面出现太多文件,要使用hash算法打散存储。
    4. 要限制上传文件的最大值。
    5. 要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

    参考文章:
    Spring MVC 实现文件的上传和下载

  • 相关阅读:
    linux网卡混杂模式打开
    Python解释器运行成功,命令运行显示无此属性解决办法
    Python-01基础-09Python相关术语
    Python-01基础-05模块
    Python-01基础-04类
    Python-01基础-03函数
    Python-01基础-02数据类型
    Python-01基础-01Python简介
    Python-01基础-00菜鸟教程
    Module-Dask并行任务调度
  • 原文地址:https://www.cnblogs.com/ooo0/p/10371188.html
Copyright © 2020-2023  润新知