目錄html
首先咱們須要準備好開發環境,本文測試環境是 SSM(Spring 4.3.9 + SpringMVC 4.3.9 + MyBatis 3.4.4) ,數據庫爲 MySQL 5.5,數據庫鏈接池 C3P0 0.9.5.2,構建包 Maven 3.5.0,Tomcat 8.5。java
限於篇幅緣由,關於 SSM 框架的整合方法,在這篇文章中就不作詳細的講解啦,有關圖片上傳和下載的相關配置,我會特別標註出來講明的。mysql
咱們假定有這樣一個很常見的需求場景:用戶註冊。web
首先咱們來作一下簡單的業務分析,在註冊頁面,用戶填寫本身的相關信息,而後選擇上傳頭像圖片,註冊成功後顯示我的信息,並將圖片顯示在頁面上。spring
一看就是一個很簡單的需求吧,那咱們就來作相應的數據準備工做吧。sql
數據庫很是簡單,就一張表:t_user數據庫
字段 | 類型 | 長度 | 主鍵 | 描述 |
---|---|---|---|---|
user_id | int | 11 | PK,自增 | 用戶表主鍵 |
user_name | varchar | 50 | 用戶名 | |
user_tel | varchar | 20 | 手機號 | |
user_password | varchar | 20 | 密碼 | |
user_pic | varchar | 255 | 用戶頭像地址 |
對應數據庫表 t_user 建立實體類:Userapache
這裏我使用 mybatis-generate 代碼生成器根據 t_user 表結構自動生成實體類 和 Mybatis 的 mapper 文件。json
User 實體類的代碼以下(省略了 getter/setter):api
package com.uzipi.entity; public class User { private Integer userId; private String userName; private String userTel; private String userPassword; private String userPic; }
生成的 dao 層 java 代碼以下:
package com.uzipi.dao; import com.uzipi.entity.User; import org.mybatis.spring.annotation.MapperScan; @MapperScan // 容許 Spring 掃描該 Mapper public interface UserMapper { // 刪除指定 key 的記錄 int deleteByPrimaryKey(Integer userId); // 插入一條記錄(完整記錄) int insert(User record); // 插入一條記錄(對象中有值時寫入字段,沒有值的置空) int insertSelective(User record); // 查詢指定 key 的記錄 User selectByPrimaryKey(Integer userId); // 將對象中的內容更新入庫(對象中有值時更新字段,沒有值的屬性不修改) int updateByPrimaryKeySelective(User record); // 將對象中的內容更新入庫(全屬性) int updateByPrimaryKey(User record); }
生成的 mapper.xml 文件內容比較多,在文章裏就不展現了,後面附件中提供了下載文件供參考。
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.uzipi</groupId> <artifactId>house</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>house Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <!-- junit 單元測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- log4j 日誌 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- MySQL 數據庫鏈接驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.24</version> </dependency> <!-- c3p0 數據庫鏈接池 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!-- spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.9.RELEASE</version> </dependency> <!-- spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.9.RELEASE</version> </dependency> <!-- spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.9.RELEASE</version> </dependency> <!-- 文件上傳 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!-- spring 支持的 json --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.7</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.4</version> </dependency> <!-- MyBatis 與 Spring 整合 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <!-- Servlet API需求包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- JSP相關 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!-- JSTL 標準標籤庫 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>house</finalName> </build> </project>
框架的整合配置 xml 文件請查看附件。
這裏我特別說明一下涉及到圖片(文件)上傳相關的 spring-mvc 配置:
<!-- 配置文件上傳 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 配置文件上傳的最大致積 10M --> <property name="maxUploadSize" value="10240000"></property> </bean>
package com.uzipi.controller; import com.uzipi.entity.User; import com.uzipi.service.UserService; import com.uzipi.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; 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 javax.servlet.http.HttpServletResponse; import java.io.*; @Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; // Spring 注入 UserService /** * 跳轉到註冊頁面 * @param model * @return */ @RequestMapping(value="/register", method = RequestMethod.GET) public String register(Model model){ /* 爲何這裏要 new 一個 User 對象? 由於咱們在 JSP 頁面中使用了 spring form 標籤 spring form 標籤的 modelAttribute 默認須要一個對象用於接收數據 這裏咱們是新增,因此用無參構造建立一個空對象(不是null) */ User user = new User(); model.addAttribute("user", user); // user 加入到 request 域 return "user/register"; // 跳轉到 user/register.jsp 頁面 } /** * 處理用戶註冊的表單請求 * @param user * @param file * @return */ @RequestMapping(value = "/register", method = RequestMethod.POST) public String doRegister(User user, @RequestParam("imgFile") MultipartFile file, Model model){ if (userService.saveRegister(user, file)){ model.addAttribute("user", user); return "user/show"; // 註冊成功,跳轉到顯示頁面 } return "redirect:/user/register"; // 註冊失敗,重定向到註冊頁面 } /** * 處理圖片顯示請求 * @param fileName */ @RequestMapping("/showPic/{fileName}.{suffix}") public void showPicture(@PathVariable("fileName") String fileName, @PathVariable("suffix") String suffix, HttpServletResponse response){ File imgFile = new File(Constants.IMG_PATH + fileName + "." + suffix); responseFile(response, imgFile); } /** * 處理圖片下載請求 * @param fileName * @param response */ @RequestMapping("/downloadPic/{fileName}.{suffix}") public void downloadPicture(@PathVariable("fileName") String fileName, @PathVariable("suffix") String suffix, HttpServletResponse response){ // 設置下載的響應頭信息 response.setHeader("Content-Disposition", "attachment;fileName=" + "headPic.jpg"); File imgFile = new File(Constants.IMG_PATH + fileName + "." + suffix); responseFile(response, imgFile); } /** * 響應輸出圖片文件 * @param response * @param imgFile */ private void responseFile(HttpServletResponse response, File imgFile) { try(InputStream is = new FileInputStream(imgFile); OutputStream os = response.getOutputStream();){ byte [] buffer = new byte[1024]; // 圖片文件流緩存池 while(is.read(buffer) != -1){ os.write(buffer); } os.flush(); } catch (IOException ioe){ ioe.printStackTrace(); } } }
在 Controller 中,有幾個地方是須要咱們注意的,否則會遇到坑:
MultipartFile
接口來接收,最好是用註解 @RequestParam("inputName")
指明該文件對應表單中的 input 標籤的 name 屬性。若是 name 都是同名的,可使用 `MultipartFile []
文件數組來接收。{fileName}.{suffix}
這段代碼將圖片名和圖片的後綴區分開,由於 GET 方式的 URL 請求地址中的 "." 點號會被看成通配符處理掉,有多種方式能夠解決。我這種方式是一種,你也能夠用 "." 轉義字符來避免其通配符的做用。response.setHeader("Content-Disposition","attachment;fileName=" + "headPic.jpg");
當設置了該響應頭時,使用 response
輸出流將會被看成附件提供給客戶端下載,反之就是將流中的內容輸出到頁面上。buffer
的大小,太小會致使下載速度變慢,過大會佔用較多的帶寬,須要考慮平衡。package com.uzipi.service; import com.uzipi.dao.UserMapper; import com.uzipi.entity.User; import com.uzipi.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.UUID; @Service public class UserService { @Autowired private UserMapper userMapper; // Spring 注入 UserMapper 對象 /** * 用戶註冊,記錄用戶信息並處理上傳的圖片 * @param user * @param file * @return */ public boolean saveRegister(User user, MultipartFile file){ if (file != null){ // 原始文件名 String originalFileName = file.getOriginalFilename(); // 獲取圖片後綴 String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); // 生成圖片存儲的名稱,UUID 避免相同圖片名衝突,並加上圖片後綴 String fileName = UUID.randomUUID().toString() + suffix; // 圖片存儲路徑 String filePath = Constants.IMG_PATH + fileName; File saveFile = new File(filePath); try { // 將上傳的文件保存到服務器文件系統 file.transferTo(saveFile); // 記錄服務器文件系統圖片名稱 user.setUserPic(fileName); } catch (IOException e) { e.printStackTrace(); } } // 持久化 user return userMapper.insertSelective(user) > 0; } /** * 查找指定 key 的 user 對象 * @param userId * @return */ public User findByUserId(int userId){ return userMapper.selectByPrimaryKey(userId); } }
Service 層中要注意的幾個問題:
spring-mvc.xml
中配置臨時目錄的位置。但存儲在臨時目錄中的圖片並不長久,重啓服務器以後會被清理掉。咱們能夠利用 MultipartFile
接口提供的 transferTo(File dest)
方法將臨時文件轉移到咱們設置的文件系統目錄中。頁面沒有加樣式,僅實現了功能,因此不是很好看啦。
註冊頁面中使用了 spring form 標籤。關於 spring form 標籤,這裏簡單提一下,在沒有 減輕 JSP 代碼工做量
的需求前提下,仍是推薦使用原生的 form 表單標籤,由於 spring form 最終仍是會被渲染成原生的 form 標籤的樣子,中間多了一道轉換,必然會下降些許頁面的渲染速度。
register.jsp 代碼以下:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE html> <html> <head> <title>用戶註冊</title> <base href="<%=request.getContextPath()%>/"/> <style> li {list-style: none;} </style> </head> <body> <form:form action="user/register" method="post" enctype="multipart/form-data" modelAttribute="user"> <li> <form:input path="userName" placeholder="用戶名"/> </li> <li> <form:password path="userPassword" placeholder="密碼"/> </li> <li> <form:input path="userTel" placeholder="手機號"/> </li> <li> <input type="file" name="imgFile" /> </li> <li> <input type="submit" value="註冊" /> </li> </form:form> </body> </html>
register.jsp 須要注意的地方:
enctype="multipart/form-data"
,這你們應該都知道吧。modelAttribute="user"
這段屬性。所以咱們要在跳轉到該頁面以前,往 request 域中添加一個 user
對象(名字能夠自定義),若是不寫上這個屬性,SpringMVC會默認給一個 "command"。modelAttribute
對象中有引用類型的成員屬性,剛好咱們要填寫的表單元素中有一個值正好是該引用對象的屬性值,咱們能夠直接使用 xxx.xxx
的形式指明該屬性值,提交表單時,springMVC 會自動幫助咱們封裝該屬性對象。用戶顯示頁面比較簡單,主要是爲了區分出 「顯示圖片」 和 「下載圖片」 兩種請求。
show.jsp 代碼以下:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <title>用戶我的信息</title> <base href="<%=request.getContextPath()%>/"/> <style> li {list-style: none;} </style> </head> <body> <h4>我的信息</h4> <li> <!-- 頭像顯示 --> <img src="user/showPic/${user.userPic}" style="width:100px; height: 100px;"/> </li> <li> 用戶名:${user.userName} </li> <li> 手機號:${user.userTel} </li> <li> <a href="user/downloadPic/${user.userPic}">下載頭像圖片</a> </li> </body> </html>
頁面比較簡單,就一個地方能夠說明下,可能有的同窗還不太明白:
我在 <head>
標籤中加入了 <base href="<%=request.getContextPath()%>/"/>
這段代碼,目的是爲了將當前頁面的相對位置定位到 webapp 的根目錄下,這樣能夠避免請求跳轉以後,出現同一個 JSP 頁面的相對路徑不同的狀況。
到這裏,關於 SpringMVC 上傳和下載圖片的步驟就算結束啦。
若是各位同窗在測試的過程當中遇到什麼問題,能夠留言、郵箱(yotow@foxmail.com)或者QQ我(281901158)。
源代碼固然是少不了的啦,java 代碼和 SQL 文件打包在一塊兒了。
百度網盤: https://pan.baidu.com/s/1c3SSvj6 密碼:goma