@

文件上传

文件上传是项目开发中最常见的功能。为了能上传文件,必须将表单的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; // upload\2\3 upload\3\5
// 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 实现文件的上传和下载

SpringMVC 文件上传下载的更多相关文章

  1. springmvc文件上传下载简单实现案例(ssm框架使用)

    springmvc文件上传下载实现起来非常简单,此springmvc上传下载案例适合已经搭建好的ssm框架(spring+springmvc+mybatis)使用,ssm框架项目的搭建我相信你们已经搭 ...

  2. SpringMVC文件上传下载(单文件、多文件)

    前言 大家好,我是bigsai,今天我们学习Springmvc的文件上传下载. 文件上传和下载是互联网web应用非常重要的组成部分,它是信息交互传输的重要渠道之一.你可能经常在网页上传下载文件,你可能 ...

  3. SpringMVC文件上传下载

    在Spring MVC的基础框架搭建起来后,我们测试了spring mvc中的返回值类型,如果你还没有搭建好springmvc的架构请参考博文->http://www.cnblogs.com/q ...

  4. SpringMVC——文件上传下载

    一.单文件上传 1.导入依赖 <dependency> <groupId>commons-io</groupId> <artifactId>common ...

  5. SpringMVC整合fastdfs-client-java实现web文件上传下载

    原文:http://blog.csdn.net/wlwlwlwl015/article/details/52682153 本篇blog主要记录一下SpringMVC整合FastDFS的Java客户端实 ...

  6. SpringMVC ajax技术无刷新文件上传下载删除示例

    参考 Spring MVC中上传文件实例 SpringMVC结合ajaxfileupload.js实现ajax无刷新文件上传 Spring MVC 文件上传下载 (FileOperateUtil.ja ...

  7. SpringMVC(三) RESTful架构和文件上传下载

    RESTful架构 REST全名为:Representational State Transfer.资源表现层状态转化.是目前最流行的一种互联网软件架构. 它结构清晰.符合标准.易于理解.扩展方便,所 ...

  8. SpringMVC——返回JSON数据&&文件上传下载

    --------------------------------------------返回JSON数据------------------------------------------------ ...

  9. SpringMVC文件上传 Excle文件 Poi解析 验证 去重 并批量导入 MYSQL数据库

    SpringMVC文件上传 Excle文件 Poi解析并批量导入 MYSQL数据库  /** * 业务需求说明: * 1 批量导入成员 并且 自主创建账号 * 2 校验数据格式 且 重复导入提示 已被 ...

随机推荐

  1. 第二章 Linux目录学习

    Linux 目录结构相对windows来说更简单,Linux 目录 以 斜杠 / 为根目录,其整体结构是以/为根的树状结构. 使用 tree -L 1 查看1级目录结构 /bin 常用的二进制命令目录 ...

  2. Django 创建一个返回当前时间的页面

    创建一个 Django 项目及应用 django-admin startproject mysite cd mysite # 手动创建一个 templates 文件夹用来保存 html 文件 mkdi ...

  3. WEB前端学习资源清单

    常用学习资源 JS参考与基础学习系列 [MDN]JS标准参考 es6教程 JS标准参考教程 编程类中文书籍索引 深入理解JS系列 前端开发仓库 <JavaScript 闯关记> JavaS ...

  4. Python笔记-高级特性

    1.迭代 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration). 如果要迭代value,可以用for value in d ...

  5. Log4j配置(xml和property两种)

    本文章为转载记录,纯粹个人备忘. 一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR 为严重错误 主要是程序的错误WARN 为一 ...

  6. centos 7 进入图形界面

    1 确保自己已经安装图形界面,如果没有请执行命令 yum groupinstall "GNOME Desktop" "Graphical Administration T ...

  7. 数论 C - Aladdin and the Flying Carpet

    It's said that Aladdin had to solve seven mysteries before getting the Magical Lamp which summons a ...

  8. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  9. IDEA 代码风格设置

    1.类注释 File -> Settings -> Editor -> File and Code Templates -> Includes -> FileHeader ...

  10. 3Openwrt自定义CGI实现 前后端交互

    https://www.cnblogs.com/163yun/p/9834993.html 安装uhttpd. 在编译openwrt前,输入make memuconfig,查找Network -> ...